@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,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
|
+
import type { PluginApiHandler } from '@aglyn/aglyn/server';
|
|
18
|
+
import * as CommerceModel from '../model';
|
|
19
|
+
export interface AccountOrderView {
|
|
20
|
+
id: string;
|
|
21
|
+
number: string;
|
|
22
|
+
status: CommerceModel.OrderStatus;
|
|
23
|
+
totalCents: number;
|
|
24
|
+
createdAtMs: number;
|
|
25
|
+
itemsSummary: string;
|
|
26
|
+
tracking?: {
|
|
27
|
+
carrier?: string;
|
|
28
|
+
trackingNumber?: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Member account (AGL-294): profile + address book + order history for
|
|
33
|
+
* the signed-in site member (AGL-109 session cookie). Orders match by
|
|
34
|
+
* the member's email — guest purchases made with the same address show
|
|
35
|
+
* up too, which is what buyers expect.
|
|
36
|
+
*/
|
|
37
|
+
export declare const membershipAccountHandler: PluginApiHandler;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
import { memberNameSearchFields } from "./member-name-search.js";
|
|
18
|
+
import * as CommerceModel from "../model/index.js";
|
|
19
|
+
import { firebaseAdmin } from "@aglyn/tenant-data-admin";
|
|
20
|
+
import { mintDownloadToken } from "./download.js";
|
|
21
|
+
import { requireActiveMember } from "./membership.js";
|
|
22
|
+
/**
|
|
23
|
+
* Member account (AGL-294): profile + address book + order history for
|
|
24
|
+
* the signed-in site member (AGL-109 session cookie). Orders match by
|
|
25
|
+
* the member's email — guest purchases made with the same address show
|
|
26
|
+
* up too, which is what buyers expect.
|
|
27
|
+
*/ export const membershipAccountHandler = async (req, res)=>{
|
|
28
|
+
var _ref;
|
|
29
|
+
var _req_body;
|
|
30
|
+
const hostId = String((_ref = req.method === 'GET' ? req.query.hostId : (_req_body = req.body) == null ? void 0 : _req_body.hostId) != null ? _ref : '');
|
|
31
|
+
if (!hostId) return res.status(400).json({
|
|
32
|
+
error: 'Missing hostId'
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
var _memberSnapshot_get, _memberSnapshot_get1, _memberSnapshot_get2;
|
|
36
|
+
// Suspension gate (AGL-546/550): the shared guard 401s stale
|
|
37
|
+
// sessions and 403s suspended members with the cookie cleared, so
|
|
38
|
+
// the site stops presenting a signed-in shell; the account block
|
|
39
|
+
// treats any non-OK response as signed-out, the graceful path here.
|
|
40
|
+
const auth = await requireActiveMember(req, res, hostId);
|
|
41
|
+
if (!auth) return;
|
|
42
|
+
const { memberId, member: memberSnapshot } = auth;
|
|
43
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
44
|
+
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
45
|
+
const memberRef = hostRef.collection('siteMembers').doc(memberId);
|
|
46
|
+
if (req.method === 'POST') {
|
|
47
|
+
var _ref1;
|
|
48
|
+
var _req_body1, _req_body2;
|
|
49
|
+
const displayName = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.displayName) != null ? _ref1 : '').slice(0, 80);
|
|
50
|
+
const addresses = Array.isArray((_req_body2 = req.body) == null ? void 0 : _req_body2.addresses) ? req.body.addresses.slice(0, 5).map((address)=>{
|
|
51
|
+
var _address_name, _address_line1, _address_line2, _address_city, _address_state, _address_postalCode, _address_country;
|
|
52
|
+
return {
|
|
53
|
+
name: String((_address_name = address.name) != null ? _address_name : '').slice(0, 80),
|
|
54
|
+
line1: String((_address_line1 = address.line1) != null ? _address_line1 : '').slice(0, 120),
|
|
55
|
+
line2: String((_address_line2 = address.line2) != null ? _address_line2 : '').slice(0, 120),
|
|
56
|
+
city: String((_address_city = address.city) != null ? _address_city : '').slice(0, 80),
|
|
57
|
+
state: String((_address_state = address.state) != null ? _address_state : '').slice(0, 40),
|
|
58
|
+
postalCode: String((_address_postalCode = address.postalCode) != null ? _address_postalCode : '').slice(0, 20),
|
|
59
|
+
country: String((_address_country = address.country) != null ? _address_country : '').slice(0, 2).toUpperCase()
|
|
60
|
+
};
|
|
61
|
+
}) : undefined;
|
|
62
|
+
await memberRef.set(_extends({}, displayName ? memberNameSearchFields(displayName) : {}, addresses ? {
|
|
63
|
+
addresses
|
|
64
|
+
} : {}), {
|
|
65
|
+
merge: true
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const email = String((_memberSnapshot_get = memberSnapshot.get('email')) != null ? _memberSnapshot_get : '');
|
|
69
|
+
const ordersSnapshot = await hostRef.collection('orders').where('customerEmail', '==', email).limit(25).get();
|
|
70
|
+
const orders = ordersSnapshot.docs.map((docSnapshot)=>{
|
|
71
|
+
var _order_fulfillments, _ref, _order_amountCents, _order_createdAtMs, _ref1, _order_lineItems;
|
|
72
|
+
var _order_totals, _docSnapshot_get;
|
|
73
|
+
const order = CommerceModel.liftLegacyOrder(docSnapshot.data());
|
|
74
|
+
const latestFulfillment = ((_order_fulfillments = order.fulfillments) != null ? _order_fulfillments : []).slice(-1)[0];
|
|
75
|
+
return _extends({
|
|
76
|
+
id: docSnapshot.id,
|
|
77
|
+
number: CommerceModel.formatOrderNumber(order, docSnapshot.id),
|
|
78
|
+
status: order.status,
|
|
79
|
+
totalCents: (_ref = (_order_totals = order.totals) == null ? void 0 : _order_totals.totalCents) != null ? _ref : Number((_order_amountCents = order.amountCents) != null ? _order_amountCents : 0),
|
|
80
|
+
createdAtMs: (_order_createdAtMs = order.createdAtMs) != null ? _order_createdAtMs : ((_ref1 = (_docSnapshot_get = docSnapshot.get('createdAt')) == null ? void 0 : _docSnapshot_get.seconds) != null ? _ref1 : 0) * 1000 || 0,
|
|
81
|
+
itemsSummary: ((_order_lineItems = order.lineItems) != null ? _order_lineItems : []).map((line)=>`${line.quantity}× ${line.name}`).join(', ').slice(0, 140)
|
|
82
|
+
}, (latestFulfillment == null ? void 0 : latestFulfillment.trackingNumber) ? {
|
|
83
|
+
tracking: {
|
|
84
|
+
carrier: latestFulfillment.carrier,
|
|
85
|
+
trackingNumber: latestFulfillment.trackingNumber
|
|
86
|
+
}
|
|
87
|
+
} : {});
|
|
88
|
+
}).sort((a, b)=>b.createdAtMs - a.createdAtMs);
|
|
89
|
+
// Digital downloads (AGL-302): tokened links for digital lines on
|
|
90
|
+
// paid orders; files resolve to the product's current list.
|
|
91
|
+
const downloads = [];
|
|
92
|
+
for (const docSnapshot of ordersSnapshot.docs){
|
|
93
|
+
var _docSnapshot_get, _order_lineItems;
|
|
94
|
+
const order = CommerceModel.liftLegacyOrder(docSnapshot.data());
|
|
95
|
+
if (!CommerceModel.orderEntitlesProduct(order, 'any')) continue;
|
|
96
|
+
const orderKeys = (_docSnapshot_get = docSnapshot.get('licenseKeys')) != null ? _docSnapshot_get : {};
|
|
97
|
+
for (const line of (_order_lineItems = order.lineItems) != null ? _order_lineItems : []){
|
|
98
|
+
var _orderKeys_line_productId;
|
|
99
|
+
if (line.productType !== 'digital') continue;
|
|
100
|
+
// PER LINE (AGL-2454). The status test above only ever moved on a FULL
|
|
101
|
+
// refund, so this page went on listing the download link AND the
|
|
102
|
+
// licence key for a line the merchant had refunded. The key string is
|
|
103
|
+
// the one thing here that cannot be taken back once mailed — which is
|
|
104
|
+
// exactly why it is retired from the pool rather than reissued — so
|
|
105
|
+
// removing it from the account page is the only withdrawal available.
|
|
106
|
+
if (!CommerceModel.orderEntitlesProduct(order, line.productId)) continue;
|
|
107
|
+
downloads.push(_extends({
|
|
108
|
+
orderId: docSnapshot.id,
|
|
109
|
+
productId: line.productId,
|
|
110
|
+
productName: line.name
|
|
111
|
+
}, ((_orderKeys_line_productId = orderKeys[line.productId]) == null ? void 0 : _orderKeys_line_productId.length) ? {
|
|
112
|
+
licenseKeys: orderKeys[line.productId]
|
|
113
|
+
} : {}, {
|
|
114
|
+
url: `/api/commerce/download?hostId=${encodeURIComponent(hostId)}` + `&orderId=${encodeURIComponent(docSnapshot.id)}` + `&productId=${encodeURIComponent(line.productId)}` + `&token=${mintDownloadToken(hostId, docSnapshot.id)}`
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Storefront subscriptions (AGL-303) with product names resolved.
|
|
119
|
+
const subscriptionsSnapshot = await hostRef.collection('subscriptions').where('customerEmail', '==', email).limit(10).get();
|
|
120
|
+
const subscriptions = await Promise.all(subscriptionsSnapshot.docs.map(async (docSnapshot)=>{
|
|
121
|
+
var _docSnapshot_get, _productSnapshot_get, _docSnapshot_get1, _docSnapshot_get2;
|
|
122
|
+
const productSnapshot = await hostRef.collection('products').doc(String((_docSnapshot_get = docSnapshot.get('productId')) != null ? _docSnapshot_get : '')).get();
|
|
123
|
+
return {
|
|
124
|
+
id: docSnapshot.id,
|
|
125
|
+
productName: String((_productSnapshot_get = productSnapshot.get('name')) != null ? _productSnapshot_get : 'Subscription'),
|
|
126
|
+
status: String((_docSnapshot_get1 = docSnapshot.get('status')) != null ? _docSnapshot_get1 : 'active'),
|
|
127
|
+
currentPeriodEndMs: (_docSnapshot_get2 = docSnapshot.get('currentPeriodEndMs')) != null ? _docSnapshot_get2 : null
|
|
128
|
+
};
|
|
129
|
+
}));
|
|
130
|
+
return res.status(200).json({
|
|
131
|
+
member: {
|
|
132
|
+
email,
|
|
133
|
+
displayName: (_memberSnapshot_get1 = memberSnapshot.get('displayName')) != null ? _memberSnapshot_get1 : '',
|
|
134
|
+
addresses: (_memberSnapshot_get2 = memberSnapshot.get('addresses')) != null ? _memberSnapshot_get2 : []
|
|
135
|
+
},
|
|
136
|
+
orders,
|
|
137
|
+
downloads,
|
|
138
|
+
subscriptions
|
|
139
|
+
});
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(error);
|
|
142
|
+
return res.status(500).json({
|
|
143
|
+
error: 'Account unavailable'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
//# sourceMappingURL=membership-account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/membership-account.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 { memberNameSearchFields } from './member-name-search'\nimport * as Aglyn from '@aglyn/aglyn/server'\nimport * as CommerceModel from '../model'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { mintDownloadToken } from './download'\nimport { requireActiveMember } from './membership'\n\nexport interface AccountOrderView {\n id: string\n number: string\n status: CommerceModel.OrderStatus\n totalCents: number\n createdAtMs: number\n itemsSummary: string\n tracking?: { carrier?: string; trackingNumber?: string }\n}\n\n/**\n * Member account (AGL-294): profile + address book + order history for\n * the signed-in site member (AGL-109 session cookie). Orders match by\n * the member's email — guest purchases made with the same address show\n * up too, which is what buyers expect.\n */\nexport const membershipAccountHandler: PluginApiHandler = async (req, res) => {\n const hostId = String(\n (req.method === 'GET' ? req.query.hostId : req.body?.hostId) ?? '',\n )\n if (!hostId) return res.status(400).json({ error: 'Missing hostId' })\n\n try {\n // Suspension gate (AGL-546/550): the shared guard 401s stale\n // sessions and 403s suspended members with the cookie cleared, so\n // the site stops presenting a signed-in shell; the account block\n // treats any non-OK response as signed-out, the graceful path here.\n const auth = await requireActiveMember(req, res, hostId)\n if (!auth) return\n const { memberId, member: memberSnapshot } = auth\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const memberRef = hostRef.collection('siteMembers').doc(memberId)\n\n if (req.method === 'POST') {\n const displayName = String(req.body?.displayName ?? '').slice(0, 80)\n const addresses = Array.isArray(req.body?.addresses)\n ? (req.body.addresses as CommerceModel.OrderAddress[]).slice(0, 5).map(\n (address) => ({\n name: String(address.name ?? '').slice(0, 80),\n line1: String(address.line1 ?? '').slice(0, 120),\n line2: String(address.line2 ?? '').slice(0, 120),\n city: String(address.city ?? '').slice(0, 80),\n state: String(address.state ?? '').slice(0, 40),\n postalCode: String(address.postalCode ?? '').slice(0, 20),\n country: String(address.country ?? '').slice(0, 2).toUpperCase(),\n }),\n )\n : undefined\n await memberRef.set(\n {\n ...(displayName ? memberNameSearchFields(displayName) : {}),\n ...(addresses ? { addresses } : {}),\n },\n { merge: true },\n )\n }\n\n const email = String(memberSnapshot.get('email') ?? '')\n const ordersSnapshot = await hostRef\n .collection('orders')\n .where('customerEmail', '==', email)\n .limit(25)\n .get()\n const orders: AccountOrderView[] = ordersSnapshot.docs\n .map((docSnapshot) => {\n const order = CommerceModel.liftLegacyOrder(docSnapshot.data() as any)\n const latestFulfillment = (order.fulfillments ?? []).slice(-1)[0]\n return {\n id: docSnapshot.id,\n number: CommerceModel.formatOrderNumber(order, docSnapshot.id),\n status: order.status,\n totalCents:\n order.totals?.totalCents ?? Number(order.amountCents ?? 0),\n createdAtMs:\n order.createdAtMs ??\n ((docSnapshot.get('createdAt')?.seconds ?? 0) * 1000 || 0),\n itemsSummary: (order.lineItems ?? [])\n .map((line) => `${line.quantity}× ${line.name}`)\n .join(', ')\n .slice(0, 140),\n ...(latestFulfillment?.trackingNumber\n ? {\n tracking: {\n carrier: latestFulfillment.carrier,\n trackingNumber: latestFulfillment.trackingNumber,\n },\n }\n : {}),\n }\n })\n .sort((a, b) => b.createdAtMs - a.createdAtMs)\n\n // Digital downloads (AGL-302): tokened links for digital lines on\n // paid orders; files resolve to the product's current list.\n const downloads: Array<{\n orderId: string\n productId: string\n productName: string\n url: string\n licenseKeys?: string[]\n }> = []\n for (const docSnapshot of ordersSnapshot.docs) {\n const order = CommerceModel.liftLegacyOrder(docSnapshot.data() as any)\n if (!CommerceModel.orderEntitlesProduct(order, 'any')) continue\n const orderKeys = (docSnapshot.get('licenseKeys') ?? {}) as Record<\n string,\n string[]\n >\n for (const line of order.lineItems ?? []) {\n if (line.productType !== 'digital') continue\n // PER LINE (AGL-2454). The status test above only ever moved on a FULL\n // refund, so this page went on listing the download link AND the\n // licence key for a line the merchant had refunded. The key string is\n // the one thing here that cannot be taken back once mailed — which is\n // exactly why it is retired from the pool rather than reissued — so\n // removing it from the account page is the only withdrawal available.\n if (!CommerceModel.orderEntitlesProduct(order, line.productId)) continue\n downloads.push({\n orderId: docSnapshot.id,\n productId: line.productId,\n productName: line.name,\n ...(orderKeys[line.productId]?.length\n ? { licenseKeys: orderKeys[line.productId] }\n : {}),\n url:\n `/api/commerce/download?hostId=${encodeURIComponent(hostId)}` +\n `&orderId=${encodeURIComponent(docSnapshot.id)}` +\n `&productId=${encodeURIComponent(line.productId)}` +\n `&token=${mintDownloadToken(hostId, docSnapshot.id)}`,\n })\n }\n }\n\n // Storefront subscriptions (AGL-303) with product names resolved.\n const subscriptionsSnapshot = await hostRef\n .collection('subscriptions')\n .where('customerEmail', '==', email)\n .limit(10)\n .get()\n const subscriptions = await Promise.all(\n subscriptionsSnapshot.docs.map(async (docSnapshot) => {\n const productSnapshot = await hostRef\n .collection('products')\n .doc(String(docSnapshot.get('productId') ?? ''))\n .get()\n return {\n id: docSnapshot.id,\n productName: String(productSnapshot.get('name') ?? 'Subscription'),\n status: String(docSnapshot.get('status') ?? 'active'),\n currentPeriodEndMs: docSnapshot.get('currentPeriodEndMs') ?? null,\n }\n }),\n )\n\n return res.status(200).json({\n member: {\n email,\n displayName: memberSnapshot.get('displayName') ?? '',\n addresses: memberSnapshot.get('addresses') ?? [],\n },\n orders,\n downloads,\n subscriptions,\n })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Account unavailable' })\n }\n}\n"],"names":["memberNameSearchFields","CommerceModel","firebaseAdmin","mintDownloadToken","requireActiveMember","membershipAccountHandler","req","res","hostId","String","method","query","body","status","json","error","memberSnapshot","auth","memberId","member","firestore","app","hostRef","collection","doc","memberRef","displayName","slice","addresses","Array","isArray","map","address","name","line1","line2","city","state","postalCode","country","toUpperCase","undefined","set","merge","email","get","ordersSnapshot","where","limit","orders","docs","docSnapshot","order","liftLegacyOrder","data","latestFulfillment","fulfillments","id","number","formatOrderNumber","totalCents","totals","Number","amountCents","createdAtMs","seconds","itemsSummary","lineItems","line","quantity","join","trackingNumber","tracking","carrier","sort","a","b","downloads","orderEntitlesProduct","orderKeys","productType","productId","push","orderId","productName","length","licenseKeys","url","encodeURIComponent","subscriptionsSnapshot","subscriptions","Promise","all","productSnapshot","currentPeriodEndMs","console"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
* Console-side password help for a SITE MEMBER (AGL-914) — a visitor account
|
|
20
|
+
* on a published site, not an Aglyn console user.
|
|
21
|
+
*
|
|
22
|
+
* Sibling to `membership-recover`, and deliberately its opposite in tone.
|
|
23
|
+
* That endpoint is public, so it answers `{ok:true}` no matter what to avoid
|
|
24
|
+
* confirming whether an address has an account. This one is driven by a site
|
|
25
|
+
* admin looking straight at the member's record — they already know the
|
|
26
|
+
* account exists, so hiding failures would only leave them guessing whether
|
|
27
|
+
* their click did anything. Every outcome here is reported honestly.
|
|
28
|
+
*
|
|
29
|
+
* Auth is the console id-token check `hosts/members` uses (site admin via
|
|
30
|
+
* the `memberRoles` projection, or an org role carrying manageMembers) — NOT
|
|
31
|
+
* the visitor session cookie the rest of the membership endpoints take.
|
|
32
|
+
*/
|
|
33
|
+
export declare const membershipAdminPasswordHandler: PluginApiHandler;
|
|
@@ -0,0 +1,202 @@
|
|
|
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 { resolveBrandingProfile, validateNewPassword } from "@aglyn/aglyn/server";
|
|
17
|
+
import { hostPublicOrigin } from "@aglyn/aglyn/server";
|
|
18
|
+
import { isEmailConfigured, sendEmail } from "@aglyn/shared-util-email";
|
|
19
|
+
import { consumePasswordResetSend, firebaseAdmin, getOrgForHost, hostSendingIdentity, isImpersonationSession, meterHostEmail, passwordResetThrottleMessage } from "@aglyn/tenant-data-admin";
|
|
20
|
+
import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
|
|
21
|
+
import { hashMemberPassword, MEMBER_SESSIONS_VALID_FROM_FIELD, mintPasswordResetToken } from "./membership.js";
|
|
22
|
+
/**
|
|
23
|
+
* Console-side password help for a SITE MEMBER (AGL-914) — a visitor account
|
|
24
|
+
* on a published site, not an Aglyn console user.
|
|
25
|
+
*
|
|
26
|
+
* Sibling to `membership-recover`, and deliberately its opposite in tone.
|
|
27
|
+
* That endpoint is public, so it answers `{ok:true}` no matter what to avoid
|
|
28
|
+
* confirming whether an address has an account. This one is driven by a site
|
|
29
|
+
* admin looking straight at the member's record — they already know the
|
|
30
|
+
* account exists, so hiding failures would only leave them guessing whether
|
|
31
|
+
* their click did anything. Every outcome here is reported honestly.
|
|
32
|
+
*
|
|
33
|
+
* Auth is the console id-token check `hosts/members` uses (site admin via
|
|
34
|
+
* the `memberRoles` projection, or an org role carrying manageMembers) — NOT
|
|
35
|
+
* the visitor session cookie the rest of the membership endpoints take.
|
|
36
|
+
*/ export const membershipAdminPasswordHandler = async (req, res)=>{
|
|
37
|
+
var _req_headers_authorization, _ref, _ref1, _ref2;
|
|
38
|
+
var _req_body, _req_body1, _req_body2;
|
|
39
|
+
if (req.method !== 'POST') {
|
|
40
|
+
return res.status(405).json({
|
|
41
|
+
error: 'Method not allowed'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const authorization = String((_req_headers_authorization = req.headers['authorization']) != null ? _req_headers_authorization : '');
|
|
45
|
+
const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : '';
|
|
46
|
+
if (!idToken) return res.status(401).json({
|
|
47
|
+
error: 'Unauthenticated'
|
|
48
|
+
});
|
|
49
|
+
const hostId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.hostId) != null ? _ref : '');
|
|
50
|
+
const memberId = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.memberId) != null ? _ref1 : '');
|
|
51
|
+
const action = String((_ref2 = (_req_body2 = req.body) == null ? void 0 : _req_body2.action) != null ? _ref2 : '');
|
|
52
|
+
if (!hostId || !memberId) {
|
|
53
|
+
return res.status(400).json({
|
|
54
|
+
error: 'Missing hostId or memberId'
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (action !== 'sendPasswordReset' && action !== 'setPassword') {
|
|
58
|
+
return res.status(400).json({
|
|
59
|
+
error: 'Unknown action'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
var _ref3, _memberSnapshot_get, _hostPublicOrigin, _ref4, _hostSnapshot_get;
|
|
64
|
+
var _this, _req_body3;
|
|
65
|
+
const app = firebaseAdmin.app();
|
|
66
|
+
const decoded = await app.auth().verifyIdToken(idToken);
|
|
67
|
+
if (!decoded.email_verified && !isImpersonationSession(decoded)) {
|
|
68
|
+
// Same shape as emailUnverifiedResponse (AGL-479), written by hand
|
|
69
|
+
// because that helper returns a fetch `Response` and this is a
|
|
70
|
+
// (req, res) plugin handler.
|
|
71
|
+
return res.status(403).json({
|
|
72
|
+
error: 'Verify your email to continue',
|
|
73
|
+
reason: 'email-unverified'
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const firestore = app.firestore();
|
|
77
|
+
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
78
|
+
const hostSnapshot = await hostRef.get();
|
|
79
|
+
const host = hostSnapshot.data();
|
|
80
|
+
const memberRole = ((_ref3 = host == null ? void 0 : host['memberRoles']) != null ? _ref3 : {})[decoded.uid];
|
|
81
|
+
const orgPermissions = await resolveOrgPermissions(decoded.uid, {
|
|
82
|
+
hostId
|
|
83
|
+
});
|
|
84
|
+
if (!host || memberRole !== 'admin' && !orgPermissions.permissions.manageMembers) {
|
|
85
|
+
return res.status(403).json({
|
|
86
|
+
error: 'Not a site admin'
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const memberRef = hostRef.collection('siteMembers').doc(memberId);
|
|
90
|
+
const memberSnapshot = await memberRef.get();
|
|
91
|
+
if (!memberSnapshot.exists) {
|
|
92
|
+
return res.status(404).json({
|
|
93
|
+
error: 'Unknown member'
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const email = String((_memberSnapshot_get = memberSnapshot.get('email')) != null ? _memberSnapshot_get : '');
|
|
97
|
+
if (!email) {
|
|
98
|
+
return res.status(400).json({
|
|
99
|
+
error: 'This member has no email address'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// `hostPublicOrigin`, not a hand-rolled apex (AGL-2195). This URL is
|
|
103
|
+
// minted server-side and mailed out, so a wrong apex is not a display
|
|
104
|
+
// bug — it is a live link on somebody else's domain.
|
|
105
|
+
const siteBase = (_hostPublicOrigin = hostPublicOrigin({
|
|
106
|
+
cname: hostSnapshot.get('cname'),
|
|
107
|
+
subdomain: hostSnapshot.get('subdomain')
|
|
108
|
+
})) != null ? _hostPublicOrigin : '';
|
|
109
|
+
const siteName = String((_ref4 = (_hostSnapshot_get = hostSnapshot.get('displayName')) != null ? _hostSnapshot_get : hostSnapshot.get('subdomain')) != null ? _ref4 : 'the site');
|
|
110
|
+
// White-label sender identity (White-Label Phase 3): the store's brand via
|
|
111
|
+
// the one shared resolver; the copy already uses the site name.
|
|
112
|
+
const branding = resolveBrandingProfile((_this = await getOrgForHost(hostId).catch(()=>null)) == null ? void 0 : _this.org);
|
|
113
|
+
if (action === 'sendPasswordReset') {
|
|
114
|
+
// Throttled per recipient and per actor (AGL-920). Same caps as the
|
|
115
|
+
// console surfaces — a site member's inbox is no less worth protecting
|
|
116
|
+
// than a console user's.
|
|
117
|
+
const throttle = await consumePasswordResetSend({
|
|
118
|
+
actorKey: decoded.uid,
|
|
119
|
+
recipientKey: email
|
|
120
|
+
});
|
|
121
|
+
if (!throttle.allowed) {
|
|
122
|
+
res.setHeader('Retry-After', String(throttle.retryAfterSeconds));
|
|
123
|
+
return res.status(429).json({
|
|
124
|
+
error: passwordResetThrottleMessage(throttle)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (!isEmailConfigured()) {
|
|
128
|
+
return res.status(502).json({
|
|
129
|
+
error: 'Email is not configured, so no reset link could be sent'
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// Same single-use, hash-bound token the self-serve flow mints
|
|
133
|
+
// (AGL-552) — completing the reset rewrites `passwordScrypt`, which
|
|
134
|
+
// retires the token without any server-side storage.
|
|
135
|
+
const token = mintPasswordResetToken(hostId, memberId, memberSnapshot.get('passwordScrypt'));
|
|
136
|
+
const resetUrl = `${siteBase}/recover?token=${encodeURIComponent(token)}`;
|
|
137
|
+
const result = await sendEmail({
|
|
138
|
+
to: email,
|
|
139
|
+
subject: `Reset your ${siteName} password`,
|
|
140
|
+
text: `An administrator of ${siteName} started a password reset for ` + 'your account. Set a new password here:\n\n' + `${resetUrl}\n\n` + 'The link works once and expires in 1 hour. Your current ' + 'password keeps working until you use it.',
|
|
141
|
+
fromName: branding.fromName,
|
|
142
|
+
sendingIdentity: await hostSendingIdentity(hostId),
|
|
143
|
+
audience: 'tenant',
|
|
144
|
+
context: 'membership admin recovery'
|
|
145
|
+
});
|
|
146
|
+
if (!result.sent) {
|
|
147
|
+
return res.status(502).json({
|
|
148
|
+
error: 'The reset email could not be delivered'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// Cost meter (AGL-1438). Transactional: a reset a quota refused leaves
|
|
152
|
+
// the member locked out of a site they pay for.
|
|
153
|
+
await meterHostEmail(hostId);
|
|
154
|
+
return res.status(200).json({
|
|
155
|
+
ok: true
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const validated = validateNewPassword((_req_body3 = req.body) == null ? void 0 : _req_body3.password);
|
|
159
|
+
if (validated.error) {
|
|
160
|
+
return res.status(400).json({
|
|
161
|
+
error: validated.error
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// Rewriting the hash alone would NOT sign the member out: their session
|
|
165
|
+
// is an HMAC cookie that never encodes the password (unlike a reset
|
|
166
|
+
// token, which binds to it on purpose). The cut-off field is what
|
|
167
|
+
// actually revokes the old sessions — see readActiveMemberSession.
|
|
168
|
+
await memberRef.update({
|
|
169
|
+
passwordScrypt: hashMemberPassword(validated.password),
|
|
170
|
+
[MEMBER_SESSIONS_VALID_FROM_FIELD]: Date.now()
|
|
171
|
+
});
|
|
172
|
+
// Best-effort: the password has already changed, so a mail failure must
|
|
173
|
+
// not fail the request and send the admin round again. Report it so
|
|
174
|
+
// they know to reach the member another way.
|
|
175
|
+
let notified = false;
|
|
176
|
+
if (isEmailConfigured()) {
|
|
177
|
+
const result = await sendEmail({
|
|
178
|
+
to: email,
|
|
179
|
+
subject: `Your ${siteName} password was changed`,
|
|
180
|
+
text: `An administrator of ${siteName} set a new password on your ` + 'account. You have been signed out on every device and will need ' + `the new password to sign back in at ${siteBase}.\n\n` + 'If you did not expect this, contact the site owner.',
|
|
181
|
+
fromName: branding.fromName,
|
|
182
|
+
sendingIdentity: await hostSendingIdentity(hostId),
|
|
183
|
+
audience: 'tenant',
|
|
184
|
+
context: 'membership admin password change'
|
|
185
|
+
});
|
|
186
|
+
notified = result.sent;
|
|
187
|
+
// Cost meter (AGL-1438). Transactional: a security notice.
|
|
188
|
+
if (result.sent) await meterHostEmail(hostId);
|
|
189
|
+
}
|
|
190
|
+
return res.status(200).json({
|
|
191
|
+
ok: true,
|
|
192
|
+
notified
|
|
193
|
+
});
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error(error);
|
|
196
|
+
return res.status(500).json({
|
|
197
|
+
error: 'Password operation failed'
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=membership-admin-password.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/membership-admin-password.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 { resolveBrandingProfile, validateNewPassword } from '@aglyn/aglyn/server'\nimport { hostPublicOrigin } from '@aglyn/aglyn/server'\nimport { isEmailConfigured, sendEmail } from '@aglyn/shared-util-email'\nimport {\n consumePasswordResetSend,\n firebaseAdmin,\n getOrgForHost,\n hostSendingIdentity,\n isImpersonationSession,\n meterHostEmail,\n passwordResetThrottleMessage,\n} from '@aglyn/tenant-data-admin'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport {\n hashMemberPassword,\n MEMBER_SESSIONS_VALID_FROM_FIELD,\n mintPasswordResetToken,\n} from './membership'\n\n/**\n * Console-side password help for a SITE MEMBER (AGL-914) — a visitor account\n * on a published site, not an Aglyn console user.\n *\n * Sibling to `membership-recover`, and deliberately its opposite in tone.\n * That endpoint is public, so it answers `{ok:true}` no matter what to avoid\n * confirming whether an address has an account. This one is driven by a site\n * admin looking straight at the member's record — they already know the\n * account exists, so hiding failures would only leave them guessing whether\n * their click did anything. Every outcome here is reported honestly.\n *\n * Auth is the console id-token check `hosts/members` uses (site admin via\n * the `memberRoles` projection, or an org role carrying manageMembers) — NOT\n * the visitor session cookie the rest of the membership endpoints take.\n */\nexport const membershipAdminPasswordHandler: PluginApiHandler = async (\n req,\n res,\n) => {\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 : ''\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n\n const hostId = String(req.body?.hostId ?? '')\n const memberId = String(req.body?.memberId ?? '')\n const action = String(req.body?.action ?? '')\n if (!hostId || !memberId) {\n return res.status(400).json({ error: 'Missing hostId or memberId' })\n }\n if (action !== 'sendPasswordReset' && action !== 'setPassword') {\n return res.status(400).json({ error: 'Unknown action' })\n }\n\n try {\n const app = firebaseAdmin.app()\n const decoded = await app.auth().verifyIdToken(idToken)\n if (!decoded.email_verified && !isImpersonationSession(decoded)) {\n // Same shape as emailUnverifiedResponse (AGL-479), written by hand\n // because that helper returns a fetch `Response` and this is a\n // (req, res) plugin handler.\n return res.status(403).json({\n error: 'Verify your email to continue',\n reason: 'email-unverified',\n })\n }\n const firestore = app.firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n const host = hostSnapshot.data()\n const memberRole = (host?.['memberRoles'] ?? {})[decoded.uid]\n const orgPermissions = await resolveOrgPermissions(decoded.uid, { hostId })\n if (\n !host ||\n (memberRole !== 'admin' && !orgPermissions.permissions.manageMembers)\n ) {\n return res.status(403).json({ error: 'Not a site admin' })\n }\n\n const memberRef = hostRef.collection('siteMembers').doc(memberId)\n const memberSnapshot = await memberRef.get()\n if (!memberSnapshot.exists) {\n return res.status(404).json({ error: 'Unknown member' })\n }\n const email = String(memberSnapshot.get('email') ?? '')\n if (!email) {\n return res.status(400).json({ error: 'This member has no email address' })\n }\n\n // `hostPublicOrigin`, not a hand-rolled apex (AGL-2195). This URL is\n // minted server-side and mailed out, so a wrong apex is not a display\n // bug — it is a live link on somebody else's domain.\n const siteBase =\n hostPublicOrigin({\n cname: hostSnapshot.get('cname'),\n subdomain: hostSnapshot.get('subdomain'),\n }) ?? ''\n const siteName = String(\n hostSnapshot.get('displayName') ?? hostSnapshot.get('subdomain') ?? 'the site',\n )\n // White-label sender identity (White-Label Phase 3): the store's brand via\n // the one shared resolver; the copy already uses the site name.\n const branding = resolveBrandingProfile(\n (await getOrgForHost(hostId).catch(() => null))?.org as never,\n )\n\n if (action === 'sendPasswordReset') {\n // Throttled per recipient and per actor (AGL-920). Same caps as the\n // console surfaces — a site member's inbox is no less worth protecting\n // than a console user's.\n const throttle = await consumePasswordResetSend({\n actorKey: decoded.uid,\n recipientKey: email,\n })\n if (!throttle.allowed) {\n res.setHeader('Retry-After', String(throttle.retryAfterSeconds))\n return res\n .status(429)\n .json({ error: passwordResetThrottleMessage(throttle) })\n }\n if (!isEmailConfigured()) {\n return res.status(502).json({\n error: 'Email is not configured, so no reset link could be sent',\n })\n }\n // Same single-use, hash-bound token the self-serve flow mints\n // (AGL-552) — completing the reset rewrites `passwordScrypt`, which\n // retires the token without any server-side storage.\n const token = mintPasswordResetToken(\n hostId,\n memberId,\n memberSnapshot.get('passwordScrypt'),\n )\n const resetUrl = `${siteBase}/recover?token=${encodeURIComponent(token)}`\n const result = await sendEmail({\n to: email,\n subject: `Reset your ${siteName} password`,\n text:\n `An administrator of ${siteName} started a password reset for ` +\n 'your account. Set a new password here:\\n\\n' +\n `${resetUrl}\\n\\n` +\n 'The link works once and expires in 1 hour. Your current ' +\n 'password keeps working until you use it.',\n fromName: branding.fromName,\n sendingIdentity: await hostSendingIdentity(hostId),\n audience: 'tenant',\n context: 'membership admin recovery',\n })\n if (!result.sent) {\n return res.status(502).json({\n error: 'The reset email could not be delivered',\n })\n }\n // Cost meter (AGL-1438). Transactional: a reset a quota refused leaves\n // the member locked out of a site they pay for.\n await meterHostEmail(hostId)\n return res.status(200).json({ ok: true })\n }\n\n const validated = validateNewPassword(req.body?.password)\n if (validated.error) {\n return res.status(400).json({ error: validated.error })\n }\n // Rewriting the hash alone would NOT sign the member out: their session\n // is an HMAC cookie that never encodes the password (unlike a reset\n // token, which binds to it on purpose). The cut-off field is what\n // actually revokes the old sessions — see readActiveMemberSession.\n await memberRef.update({\n passwordScrypt: hashMemberPassword(validated.password),\n [MEMBER_SESSIONS_VALID_FROM_FIELD]: Date.now(),\n })\n // Best-effort: the password has already changed, so a mail failure must\n // not fail the request and send the admin round again. Report it so\n // they know to reach the member another way.\n let notified = false\n if (isEmailConfigured()) {\n const result = await sendEmail({\n to: email,\n subject: `Your ${siteName} password was changed`,\n text:\n `An administrator of ${siteName} set a new password on your ` +\n 'account. You have been signed out on every device and will need ' +\n `the new password to sign back in at ${siteBase}.\\n\\n` +\n 'If you did not expect this, contact the site owner.',\n fromName: branding.fromName,\n sendingIdentity: await hostSendingIdentity(hostId),\n audience: 'tenant',\n context: 'membership admin password change',\n })\n notified = result.sent\n // Cost meter (AGL-1438). Transactional: a security notice.\n if (result.sent) await meterHostEmail(hostId)\n }\n return res.status(200).json({ ok: true, notified })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Password operation failed' })\n }\n}\n"],"names":["resolveBrandingProfile","validateNewPassword","hostPublicOrigin","isEmailConfigured","sendEmail","consumePasswordResetSend","firebaseAdmin","getOrgForHost","hostSendingIdentity","isImpersonationSession","meterHostEmail","passwordResetThrottleMessage","resolveOrgPermissions","hashMemberPassword","MEMBER_SESSIONS_VALID_FROM_FIELD","mintPasswordResetToken","membershipAdminPasswordHandler","req","res","method","status","json","error","authorization","String","headers","idToken","startsWith","slice","length","hostId","body","memberId","action","memberSnapshot","hostSnapshot","app","decoded","auth","verifyIdToken","email_verified","reason","firestore","hostRef","collection","doc","get","host","data","memberRole","uid","orgPermissions","permissions","manageMembers","memberRef","exists","email","siteBase","cname","subdomain","siteName","branding","catch","org","throttle","actorKey","recipientKey","allowed","setHeader","retryAfterSeconds","token","resetUrl","encodeURIComponent","result","to","subject","text","fromName","sendingIdentity","audience","context","sent","ok","validated","password","update","passwordScrypt","Date","now","notified","console"],"mappings":"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"}
|
|
@@ -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
|
+
* Members-only screen content (AGL-109/309): like the AGL-87 unlock API,
|
|
20
|
+
* the node tree never ships in static HTML — it is returned here only when
|
|
21
|
+
* the visitor's session cookie verifies for this host.
|
|
22
|
+
*/
|
|
23
|
+
export declare const membershipContentHandler: PluginApiHandler;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
import composeScreenNodes from "@aglyn/tenant-runtime/compose-screen-nodes";
|
|
18
|
+
import { enrichGatedScreenPage } from "@aglyn/tenant-runtime/enrich-gated-page";
|
|
19
|
+
import getScreen from "@aglyn/tenant-runtime/get-screen";
|
|
20
|
+
import { getHostDocAdmin } from "@aglyn/tenant-data-admin";
|
|
21
|
+
import { hostHasContentGating } from "./gate.js";
|
|
22
|
+
import { requireActiveMember } from "./membership.js";
|
|
23
|
+
/**
|
|
24
|
+
* Members-only screen content (AGL-109/309): like the AGL-87 unlock API,
|
|
25
|
+
* the node tree never ships in static HTML — it is returned here only when
|
|
26
|
+
* the visitor's session cookie verifies for this host.
|
|
27
|
+
*/ export const membershipContentHandler = async (req, res)=>{
|
|
28
|
+
var _ref, _ref1;
|
|
29
|
+
var _req_body, _req_body1;
|
|
30
|
+
if (req.method !== 'POST') {
|
|
31
|
+
return res.status(405).json({
|
|
32
|
+
error: 'Method not allowed'
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const hostId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.hostId) != null ? _ref : '');
|
|
36
|
+
const screenId = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.screenId) != null ? _ref1 : '');
|
|
37
|
+
if (!hostId || !screenId) {
|
|
38
|
+
return res.status(400).json({
|
|
39
|
+
error: 'Invalid request'
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
// Suspension gate (AGL-550): the cookie alone no longer unlocks the
|
|
44
|
+
// node tree — a suspended member (AGL-546) is 403'd, cookie cleared.
|
|
45
|
+
const auth = await requireActiveMember(req, res, hostId, 'Sign in required');
|
|
46
|
+
if (!auth) return;
|
|
47
|
+
// Org-level gate (AGL-481): members-only screens are a Business+
|
|
48
|
+
// feature. If the host's plan doesn't include content gating, the
|
|
49
|
+
// protected node tree is never served — fail closed. (These nodes
|
|
50
|
+
// never ship in static HTML, so this endpoint is the runtime gate.)
|
|
51
|
+
if (!await hostHasContentGating(hostId)) {
|
|
52
|
+
return res.status(403).json({
|
|
53
|
+
error: 'Members-only content is unavailable'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const screenRes = await getScreen({
|
|
57
|
+
hostId,
|
|
58
|
+
screenId
|
|
59
|
+
});
|
|
60
|
+
if (!screenRes.screen) {
|
|
61
|
+
return res.status(404).json({
|
|
62
|
+
error: 'Unknown screen'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// The site document (AGL-2883): the tree's host variables fill in from
|
|
66
|
+
// it, and the enricher slice below reads the same copy. A failed read
|
|
67
|
+
// composes with no site, which renders those variables as nothing — the
|
|
68
|
+
// member's content without its business name still beats no content.
|
|
69
|
+
const host = await getHostDocAdmin(hostId).catch((error)=>{
|
|
70
|
+
console.error(error);
|
|
71
|
+
return null;
|
|
72
|
+
});
|
|
73
|
+
const nodes = await composeScreenNodes({
|
|
74
|
+
hostId,
|
|
75
|
+
screenId,
|
|
76
|
+
screen: screenRes.screen,
|
|
77
|
+
host
|
|
78
|
+
});
|
|
79
|
+
if (!nodes) return res.status(404).json({
|
|
80
|
+
error: 'No published version'
|
|
81
|
+
});
|
|
82
|
+
// The page's behavior travels with its nodes (AGL-2510). The loader ships
|
|
83
|
+
// `nodes: null` for a members-only screen, so its enricher slice — the
|
|
84
|
+
// interactions the shared layout's nav opens on, above all — has nowhere
|
|
85
|
+
// else to arrive from, and the member saw a nav that did not open.
|
|
86
|
+
const enriched = await enrichGatedScreenPage(_extends({
|
|
87
|
+
hostId,
|
|
88
|
+
screenId,
|
|
89
|
+
screen: screenRes.screen,
|
|
90
|
+
nodes
|
|
91
|
+
}, host ? {
|
|
92
|
+
host
|
|
93
|
+
} : {}));
|
|
94
|
+
return res.status(200).json(_extends({
|
|
95
|
+
nodes
|
|
96
|
+
}, enriched));
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(error);
|
|
99
|
+
return res.status(500).json({
|
|
100
|
+
error: 'Content fetch failed'
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=membership-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/membership-content.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 composeScreenNodes from '@aglyn/tenant-runtime/compose-screen-nodes'\nimport { enrichGatedScreenPage } from '@aglyn/tenant-runtime/enrich-gated-page'\nimport getScreen from '@aglyn/tenant-runtime/get-screen'\nimport { getHostDocAdmin } from '@aglyn/tenant-data-admin'\nimport { hostHasContentGating } from './gate'\nimport { requireActiveMember } from './membership'\n\n/**\n * Members-only screen content (AGL-109/309): like the AGL-87 unlock API,\n * the node tree never ships in static HTML — it is returned here only when\n * the visitor's session cookie verifies for this host.\n */\nexport const membershipContentHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const hostId = String(req.body?.hostId ?? '')\n const screenId = String(req.body?.screenId ?? '')\n if (!hostId || !screenId) {\n return res.status(400).json({ error: 'Invalid request' })\n }\n try {\n // Suspension gate (AGL-550): the cookie alone no longer unlocks the\n // node tree — a suspended member (AGL-546) is 403'd, cookie cleared.\n const auth = await requireActiveMember(req, res, hostId, 'Sign in required')\n if (!auth) return\n // Org-level gate (AGL-481): members-only screens are a Business+\n // feature. If the host's plan doesn't include content gating, the\n // protected node tree is never served — fail closed. (These nodes\n // never ship in static HTML, so this endpoint is the runtime gate.)\n if (!(await hostHasContentGating(hostId))) {\n return res.status(403).json({ error: 'Members-only content is unavailable' })\n }\n const screenRes = await getScreen({ hostId, screenId })\n if (!screenRes.screen) {\n return res.status(404).json({ error: 'Unknown screen' })\n }\n // The site document (AGL-2883): the tree's host variables fill in from\n // it, and the enricher slice below reads the same copy. A failed read\n // composes with no site, which renders those variables as nothing — the\n // member's content without its business name still beats no content.\n const host = await getHostDocAdmin(hostId).catch((error) => {\n console.error(error)\n return null\n })\n const nodes = await composeScreenNodes({\n hostId,\n screenId,\n screen: screenRes.screen,\n host,\n })\n if (!nodes) return res.status(404).json({ error: 'No published version' })\n // The page's behavior travels with its nodes (AGL-2510). The loader ships\n // `nodes: null` for a members-only screen, so its enricher slice — the\n // interactions the shared layout's nav opens on, above all — has nowhere\n // else to arrive from, and the member saw a nav that did not open.\n const enriched = await enrichGatedScreenPage({\n hostId,\n screenId,\n screen: screenRes.screen,\n nodes,\n ...(host ? { host } : {}),\n })\n return res.status(200).json({ nodes, ...enriched })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Content fetch failed' })\n }\n}\n"],"names":["composeScreenNodes","enrichGatedScreenPage","getScreen","getHostDocAdmin","hostHasContentGating","requireActiveMember","membershipContentHandler","req","res","method","status","json","error","hostId","String","body","screenId","auth","screenRes","screen","host","catch","console","nodes","enriched"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,OAAOA,wBAAwB,6CAA4C;AAC3E,SAASC,qBAAqB,QAAQ,0CAAyC;AAC/E,OAAOC,eAAe,mCAAkC;AACxD,SAASC,eAAe,QAAQ,2BAA0B;AAC1D,SAASC,oBAAoB,QAAQ,YAAQ;AAC7C,SAASC,mBAAmB,QAAQ,kBAAc;AAElD;;;;CAIC,GACD,OAAO,MAAMC,2BAA6C,OAAOC,KAAKC;;QAI9CD,WACEA;IAJxB,IAAIA,IAAIE,MAAM,KAAK,QAAQ;QACzB,OAAOD,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAqB;IAC5D;IACA,MAAMC,SAASC,gBAAOP,YAAAA,IAAIQ,IAAI,qBAARR,UAAUM,MAAM,mBAAI;IAC1C,MAAMG,WAAWF,iBAAOP,aAAAA,IAAIQ,IAAI,qBAARR,WAAUS,QAAQ,oBAAI;IAC9C,IAAI,CAACH,UAAU,CAACG,UAAU;QACxB,OAAOR,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAkB;IACzD;IACA,IAAI;QACF,oEAAoE;QACpE,qEAAqE;QACrE,MAAMK,OAAO,MAAMZ,oBAAoBE,KAAKC,KAAKK,QAAQ;QACzD,IAAI,CAACI,MAAM;QACX,iEAAiE;QACjE,kEAAkE;QAClE,kEAAkE;QAClE,oEAAoE;QACpE,IAAI,CAAE,MAAMb,qBAAqBS,SAAU;YACzC,OAAOL,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;gBAAEC,OAAO;YAAsC;QAC7E;QACA,MAAMM,YAAY,MAAMhB,UAAU;YAAEW;YAAQG;QAAS;QACrD,IAAI,CAACE,UAAUC,MAAM,EAAE;YACrB,OAAOX,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;gBAAEC,OAAO;YAAiB;QACxD;QACA,uEAAuE;QACvE,sEAAsE;QACtE,wEAAwE;QACxE,qEAAqE;QACrE,MAAMQ,OAAO,MAAMjB,gBAAgBU,QAAQQ,KAAK,CAAC,CAACT;YAChDU,QAAQV,KAAK,CAACA;YACd,OAAO;QACT;QACA,MAAMW,QAAQ,MAAMvB,mBAAmB;YACrCa;YACAG;YACAG,QAAQD,UAAUC,MAAM;YACxBC;QACF;QACA,IAAI,CAACG,OAAO,OAAOf,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAuB;QACxE,0EAA0E;QAC1E,uEAAuE;QACvE,yEAAyE;QACzE,mEAAmE;QACnE,MAAMY,WAAW,MAAMvB,sBAAsB;YAC3CY;YACAG;YACAG,QAAQD,UAAUC,MAAM;YACxBI;WACIH,OAAO;YAAEA;QAAK,IAAI,CAAC;QAEzB,OAAOZ,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEY;WAAUC;IAC1C,EAAE,OAAOZ,OAAO;QACdU,QAAQV,KAAK,CAACA;QACd,OAAOJ,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAuB;IAC9D;AACF,EAAC"}
|