@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,241 @@
|
|
|
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 { checkVisitorRecordCeiling, HOST_TOKENS, marketingConsentFieldsForHost, personKey, SITE_MEMBER_CEILING_CODE, SITE_MEMBER_UNAVAILABLE_MESSAGE, SITE_MEMBERS_MAX_PER_HOST } from "@aglyn/aglyn/server";
|
|
19
|
+
import { addHostLead, firebaseAdmin, recordVisitorRecordCeilingTrip, resolveCampaignTouch } from "@aglyn/tenant-data-admin";
|
|
20
|
+
import { captureHostContact, emitHostEvent } from "@aglyn/tenant-runtime";
|
|
21
|
+
import { hashMemberPassword, mintMemberSession, setMemberCookie } from "./membership.js";
|
|
22
|
+
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
23
|
+
/**
|
|
24
|
+
* Site member sign-up (AGL-109): creates the member record (scrypt hash),
|
|
25
|
+
* doubles as a lead, and signs the visitor in via the session cookie.
|
|
26
|
+
*/ export const membershipRegisterHandler = async (req, res)=>{
|
|
27
|
+
var _ref, _ref1, _ref2, _ref3;
|
|
28
|
+
var _req_body, _req_body1, _req_body2, _req_body3, _req_body4;
|
|
29
|
+
if (req.method !== 'POST') {
|
|
30
|
+
return res.status(405).json({
|
|
31
|
+
error: 'Method not allowed'
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const hostId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.hostId) != null ? _ref : '');
|
|
35
|
+
const email = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.email) != null ? _ref1 : '').trim().toLowerCase();
|
|
36
|
+
const password = String((_ref2 = (_req_body2 = req.body) == null ? void 0 : _req_body2.password) != null ? _ref2 : '');
|
|
37
|
+
const displayName = String((_ref3 = (_req_body3 = req.body) == null ? void 0 : _req_body3.displayName) != null ? _ref3 : '').trim().slice(0, 80);
|
|
38
|
+
// Explicit opt-in checkbox (AGL-2499) — never inferred from signing up.
|
|
39
|
+
const marketingConsent = ((_req_body4 = req.body) == null ? void 0 : _req_body4.marketingConsent) === true;
|
|
40
|
+
if (!hostId) return res.status(400).json({
|
|
41
|
+
error: 'Missing host'
|
|
42
|
+
});
|
|
43
|
+
if (!EMAIL_PATTERN.test(email)) {
|
|
44
|
+
return res.status(400).json({
|
|
45
|
+
error: 'Enter a valid email'
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (password.length < 8) {
|
|
49
|
+
return res.status(400).json({
|
|
50
|
+
error: 'Password must be at least 8 characters'
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
var _personKey;
|
|
55
|
+
var _req_body5;
|
|
56
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
57
|
+
const hostRef = firestore.collection('hosts').doc(hostId);
|
|
58
|
+
const hostSnapshot = await hostRef.get();
|
|
59
|
+
if (!hostSnapshot.exists) {
|
|
60
|
+
return res.status(404).json({
|
|
61
|
+
error: 'Unknown site'
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Member accounts are UNLIMITED on every plan (AGL-889) — no seat or
|
|
65
|
+
// quota check belongs here. Audience monetization happens downstream:
|
|
66
|
+
// contact bands meter the CRM projection (AGL-890) and paid
|
|
67
|
+
// memberships carry the plan's digital transaction fee (AGL-892).
|
|
68
|
+
//
|
|
69
|
+
// `SITE_MEMBERS_MAX_PER_HOST` below is NOT that check and does not make
|
|
70
|
+
// it one (AGL-1529). It is a flat PLATFORM ceiling — same number on every
|
|
71
|
+
// plan, no `OrgEntitlements` key, nothing on the price list — and it
|
|
72
|
+
// exists because this handler is reachable by an ANONYMOUS VISITOR, so
|
|
73
|
+
// without it the collection was bounded only by a per-(host, IP) rate
|
|
74
|
+
// limiter that fails soft and bounds the RATE rather than the TOTAL. An
|
|
75
|
+
// abuse control is not something we sell; "unlimited member accounts on
|
|
76
|
+
// every plan" stays literally true.
|
|
77
|
+
const membersRef = hostRef.collection('siteMembers');
|
|
78
|
+
const memberRef = membersRef.doc();
|
|
79
|
+
/*
|
|
80
|
+
* COUNT, DEDUPE AND CREATE IN ONE TRANSACTION (AGL-1529, the AGL-2231
|
|
81
|
+
* treatment).
|
|
82
|
+
*
|
|
83
|
+
* Read-then-decide-then-`set()` is the create-time quota that laundered
|
|
84
|
+
* everywhere else in this repo: N concurrent sign-ups each read the same
|
|
85
|
+
* pre-count, each find room, and each land — and nothing re-counts
|
|
86
|
+
* afterwards, so the extra accounts are permanent. The fix is WHEN the
|
|
87
|
+
* count is evaluated, not the counting rule.
|
|
88
|
+
* `Transaction.get(AggregateQuery)` serialises the count against a
|
|
89
|
+
* concurrent create into this collection: the loser retries, re-reads the
|
|
90
|
+
* higher count and is refused. The duplicate-email read moves inside for
|
|
91
|
+
* free, which also closes the smaller race it always had — two
|
|
92
|
+
* simultaneous sign-ups on one address both saw `empty` and both wrote.
|
|
93
|
+
*
|
|
94
|
+
* ALL READS BEFORE THE WRITE, which Firestore requires.
|
|
95
|
+
*
|
|
96
|
+
* The scrypt hash sits between the last read and the create ON PURPOSE.
|
|
97
|
+
* It is ~100 ms of CPU and it is the most expensive thing in the request,
|
|
98
|
+
* so a refused sign-up must never pay it — hashing before the transaction
|
|
99
|
+
* would hand a flood a CPU amplifier on exactly the path the ceiling
|
|
100
|
+
* exists to contain, and would also be a regression, since today a
|
|
101
|
+
* duplicate-email refusal does not hash either. It is a pure computation,
|
|
102
|
+
* not an effect, so it does not violate the "nothing happens in a
|
|
103
|
+
* transaction body" rule the sibling routes state.
|
|
104
|
+
*
|
|
105
|
+
* A refusal is returned as DATA and rendered outside: a body that can run
|
|
106
|
+
* several times must not be the place a response is built.
|
|
107
|
+
*/ const refusal = await firestore.runTransaction(async (tx)=>{
|
|
108
|
+
const existing = await tx.get(membersRef.where('email', '==', email).limit(1));
|
|
109
|
+
if (!existing.empty) return {
|
|
110
|
+
duplicate: true,
|
|
111
|
+
ceiling: 0
|
|
112
|
+
};
|
|
113
|
+
const used = (await tx.get(membersRef.count())).data().count;
|
|
114
|
+
// Live documents only, so removing a member in the inbox frees the slot.
|
|
115
|
+
const verdict = checkVisitorRecordCeiling(used, SITE_MEMBERS_MAX_PER_HOST);
|
|
116
|
+
if (verdict.exceeded) return {
|
|
117
|
+
duplicate: false,
|
|
118
|
+
ceiling: verdict.ceiling
|
|
119
|
+
};
|
|
120
|
+
tx.create(memberRef, _extends({
|
|
121
|
+
email
|
|
122
|
+
}, displayName ? memberNameSearchFields(displayName) : {}, {
|
|
123
|
+
passwordScrypt: hashMemberPassword(password)
|
|
124
|
+
}, marketingConsent ? marketingConsentFieldsForHost(hostId, Date.now()) : {}, {
|
|
125
|
+
createdAt: firebaseAdmin.firestore.FieldValue.serverTimestamp()
|
|
126
|
+
}));
|
|
127
|
+
return null;
|
|
128
|
+
});
|
|
129
|
+
if (refusal == null ? void 0 : refusal.duplicate) {
|
|
130
|
+
return res.status(409).json({
|
|
131
|
+
error: 'That email is already a member'
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (refusal) {
|
|
135
|
+
var _HOST_TOKENS_supportEmail;
|
|
136
|
+
// Visible to the HOST — the rule that a control nobody can see in
|
|
137
|
+
// the console did not ship. Durable counter + one notification.
|
|
138
|
+
await recordVisitorRecordCeilingTrip({
|
|
139
|
+
hostRef,
|
|
140
|
+
hostId,
|
|
141
|
+
kind: 'siteMembers',
|
|
142
|
+
ceiling: refusal.ceiling
|
|
143
|
+
});
|
|
144
|
+
// Opaque to the VISITOR, who is a stranger to this site and not our
|
|
145
|
+
// customer (AGL-1666's rules, restated in the message's own docblock).
|
|
146
|
+
// The one genuinely useful thing to hand them is a door that still
|
|
147
|
+
// opens, so the site's OWN published support address rides along when
|
|
148
|
+
// it has one — read through the host-token registry, whose description
|
|
149
|
+
// of this field is literally "where visitors should write for help".
|
|
150
|
+
// Nothing else off the host document may leave the console.
|
|
151
|
+
const contact = (_HOST_TOKENS_supportEmail = HOST_TOKENS['supportEmail']) == null ? void 0 : _HOST_TOKENS_supportEmail.resolve(hostSnapshot.data());
|
|
152
|
+
return res.status(429).json(_extends({
|
|
153
|
+
error: SITE_MEMBER_UNAVAILABLE_MESSAGE,
|
|
154
|
+
// Machine-readable, because the dispatcher's rate limiter answers 429
|
|
155
|
+
// too and the status therefore discriminates nothing.
|
|
156
|
+
code: SITE_MEMBER_CEILING_CODE
|
|
157
|
+
}, contact ? {
|
|
158
|
+
contact
|
|
159
|
+
} : {}));
|
|
160
|
+
}
|
|
161
|
+
// Sign-ups double as leads for the site owner (AGL-109), through the one
|
|
162
|
+
// writer that enforces `LEADS_MAX_PER_HOST` (AGL-1529). A refused lead
|
|
163
|
+
// never fails the sign-up: the visitor asked for an account, not for a
|
|
164
|
+
// lead record, and the trip is recorded for the owner either way.
|
|
165
|
+
/*
|
|
166
|
+
* THE CAMPAIGN TOUCH, RESOLVED ONCE FOR THE WHOLE SIGN-UP.
|
|
167
|
+
*
|
|
168
|
+
* A sign-up is the identify moment for a visitor who has been anonymous
|
|
169
|
+
* until now, and it writes two records that a campaign can be credited
|
|
170
|
+
* with — the lead and the contact. One resolve, one keyed read, and the
|
|
171
|
+
* two cannot end up naming different campaigns.
|
|
172
|
+
*
|
|
173
|
+
* The MEMBER record itself is not attributed. A member is an account the
|
|
174
|
+
* visitor holds and the lead is the site's record of the same act, so
|
|
175
|
+
* crediting both would count one sign-up twice under two names.
|
|
176
|
+
*/ const signedUpAtMs = Date.now();
|
|
177
|
+
const campaignTouch = await resolveCampaignTouch({
|
|
178
|
+
hostId,
|
|
179
|
+
wire: (_req_body5 = req.body) == null ? void 0 : _req_body5.campaignTouch,
|
|
180
|
+
email,
|
|
181
|
+
atMs: signedUpAtMs
|
|
182
|
+
});
|
|
183
|
+
await addHostLead(_extends({
|
|
184
|
+
hostRef,
|
|
185
|
+
hostId,
|
|
186
|
+
lead: _extends({
|
|
187
|
+
email
|
|
188
|
+
}, displayName ? {
|
|
189
|
+
name: displayName
|
|
190
|
+
} : {}, {
|
|
191
|
+
source: 'signup'
|
|
192
|
+
}, marketingConsent ? {
|
|
193
|
+
marketingConsent: true
|
|
194
|
+
} : {})
|
|
195
|
+
}, campaignTouch ? {
|
|
196
|
+
touch: campaignTouch
|
|
197
|
+
} : {}));
|
|
198
|
+
// Contacts ingestion (AGL-197).
|
|
199
|
+
void captureHostContact(_extends({
|
|
200
|
+
hostId,
|
|
201
|
+
email,
|
|
202
|
+
name: displayName || undefined,
|
|
203
|
+
source: 'member',
|
|
204
|
+
// An account is a subscription to the site, not an enquiry (AGL-2612):
|
|
205
|
+
// the lead this sign-up also files is the sales record, and the stage
|
|
206
|
+
// on the contact says only that the person asked to be kept.
|
|
207
|
+
initialLifecycleStage: 'subscriber',
|
|
208
|
+
interaction: {
|
|
209
|
+
refId: memberRef.id,
|
|
210
|
+
summary: 'Joined as a member'
|
|
211
|
+
}
|
|
212
|
+
}, marketingConsent ? {
|
|
213
|
+
marketingConsent: true
|
|
214
|
+
} : {}, campaignTouch ? {
|
|
215
|
+
campaignTouch
|
|
216
|
+
} : {}));
|
|
217
|
+
// Event triggers (AGL-128/148): sign-ups double as leads here too. The
|
|
218
|
+
// lead's id is the person key `addHostLead` filed it under, so a webhook
|
|
219
|
+
// can read the row back over `/v1/leads/{leadId}` (AGL-2627); empty for
|
|
220
|
+
// an address that could not be keyed, as every optional payload key is.
|
|
221
|
+
await emitHostEvent(hostId, 'memberSignUp', {
|
|
222
|
+
email
|
|
223
|
+
});
|
|
224
|
+
await emitHostEvent(hostId, 'lead', {
|
|
225
|
+
email,
|
|
226
|
+
source: 'signup',
|
|
227
|
+
leadId: (_personKey = personKey(email)) != null ? _personKey : ''
|
|
228
|
+
});
|
|
229
|
+
setMemberCookie(res, hostId, mintMemberSession(hostId, memberRef.id));
|
|
230
|
+
return res.status(200).json({
|
|
231
|
+
ok: true
|
|
232
|
+
});
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error(error);
|
|
235
|
+
return res.status(500).json({
|
|
236
|
+
error: 'Sign-up failed'
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
//# sourceMappingURL=membership-register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/membership-register.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 {\n checkVisitorRecordCeiling,\n HOST_TOKENS,\n marketingConsentFieldsForHost,\n personKey,\n SITE_MEMBER_CEILING_CODE,\n SITE_MEMBER_UNAVAILABLE_MESSAGE,\n SITE_MEMBERS_MAX_PER_HOST,\n} from '@aglyn/aglyn/server'\nimport {\n addHostLead,\n firebaseAdmin,\n recordVisitorRecordCeilingTrip,\n resolveCampaignTouch,\n} from '@aglyn/tenant-data-admin'\nimport { captureHostContact, emitHostEvent } from '@aglyn/tenant-runtime'\nimport {\n hashMemberPassword,\n mintMemberSession,\n setMemberCookie,\n} from './membership'\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\n/**\n * Site member sign-up (AGL-109): creates the member record (scrypt hash),\n * doubles as a lead, and signs the visitor in via the session cookie.\n */\nexport const membershipRegisterHandler: 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 email = String(req.body?.email ?? '')\n .trim()\n .toLowerCase()\n const password = String(req.body?.password ?? '')\n const displayName = String(req.body?.displayName ?? '')\n .trim()\n .slice(0, 80)\n // Explicit opt-in checkbox (AGL-2499) — never inferred from signing up.\n const marketingConsent = req.body?.marketingConsent === true\n if (!hostId) return res.status(400).json({ error: 'Missing host' })\n if (!EMAIL_PATTERN.test(email)) {\n return res.status(400).json({ error: 'Enter a valid email' })\n }\n if (password.length < 8) {\n return res\n .status(400)\n .json({ error: 'Password must be at least 8 characters' })\n }\n try {\n const firestore = firebaseAdmin.app().firestore()\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) {\n return res.status(404).json({ error: 'Unknown site' })\n }\n // Member accounts are UNLIMITED on every plan (AGL-889) — no seat or\n // quota check belongs here. Audience monetization happens downstream:\n // contact bands meter the CRM projection (AGL-890) and paid\n // memberships carry the plan's digital transaction fee (AGL-892).\n //\n // `SITE_MEMBERS_MAX_PER_HOST` below is NOT that check and does not make\n // it one (AGL-1529). It is a flat PLATFORM ceiling — same number on every\n // plan, no `OrgEntitlements` key, nothing on the price list — and it\n // exists because this handler is reachable by an ANONYMOUS VISITOR, so\n // without it the collection was bounded only by a per-(host, IP) rate\n // limiter that fails soft and bounds the RATE rather than the TOTAL. An\n // abuse control is not something we sell; \"unlimited member accounts on\n // every plan\" stays literally true.\n const membersRef = hostRef.collection('siteMembers')\n const memberRef = membersRef.doc()\n /*\n * COUNT, DEDUPE AND CREATE IN ONE TRANSACTION (AGL-1529, the AGL-2231\n * treatment).\n *\n * Read-then-decide-then-`set()` is the create-time quota that laundered\n * everywhere else in this repo: N concurrent sign-ups each read the same\n * pre-count, each find room, and each land — and nothing re-counts\n * afterwards, so the extra accounts are permanent. The fix is WHEN the\n * count is evaluated, not the counting rule.\n * `Transaction.get(AggregateQuery)` serialises the count against a\n * concurrent create into this collection: the loser retries, re-reads the\n * higher count and is refused. The duplicate-email read moves inside for\n * free, which also closes the smaller race it always had — two\n * simultaneous sign-ups on one address both saw `empty` and both wrote.\n *\n * ALL READS BEFORE THE WRITE, which Firestore requires.\n *\n * The scrypt hash sits between the last read and the create ON PURPOSE.\n * It is ~100 ms of CPU and it is the most expensive thing in the request,\n * so a refused sign-up must never pay it — hashing before the transaction\n * would hand a flood a CPU amplifier on exactly the path the ceiling\n * exists to contain, and would also be a regression, since today a\n * duplicate-email refusal does not hash either. It is a pure computation,\n * not an effect, so it does not violate the \"nothing happens in a\n * transaction body\" rule the sibling routes state.\n *\n * A refusal is returned as DATA and rendered outside: a body that can run\n * several times must not be the place a response is built.\n */\n const refusal = await firestore.runTransaction(async (tx) => {\n const existing = await tx.get(membersRef.where('email', '==', email).limit(1))\n if (!existing.empty) return { duplicate: true, ceiling: 0 }\n const used = (await tx.get(membersRef.count())).data().count\n // Live documents only, so removing a member in the inbox frees the slot.\n const verdict = checkVisitorRecordCeiling(used, SITE_MEMBERS_MAX_PER_HOST)\n if (verdict.exceeded) return { duplicate: false, ceiling: verdict.ceiling }\n tx.create(memberRef, {\n email,\n ...(displayName ? memberNameSearchFields(displayName) : {}),\n passwordScrypt: hashMemberPassword(password),\n /*\n * The checkbox is PERSISTED on the member, not only forwarded.\n *\n * It reached the lead and the contact from the two lines below and\n * was dropped for this document, so `hosts/{hostId}/siteMembers` had\n * no consent field of any kind and `audience: 'members'` had nothing\n * for the send-time join to read — the audience could not be filtered\n * even in principle (`docs/specs/email-overhaul.md` §1d/§3f). The\n * other two documents are not a substitute: a member is deduped in\n * this transaction while leads append every time, and contacts are\n * ORG-scoped where a member is the site's own.\n *\n * Written only when ticked. Signing up is not opting in — that is why\n * the checkbox exists and why it defaults unchecked — so the omitted\n * case stores nothing and reads back as an unrecorded basis rather\n * than as a refusal.\n *\n * Under this site's key even though `siteMembers` already lives\n * beneath it, so that one reader answers for every silo — see\n * `addHostLead`, which makes the same write for the same reason.\n */\n ...(marketingConsent\n ? marketingConsentFieldsForHost(hostId, Date.now())\n : {}),\n createdAt: firebaseAdmin.firestore.FieldValue.serverTimestamp(),\n })\n return null\n })\n if (refusal?.duplicate) {\n return res.status(409).json({ error: 'That email is already a member' })\n }\n if (refusal) {\n // Visible to the HOST — the rule that a control nobody can see in\n // the console did not ship. Durable counter + one notification.\n await recordVisitorRecordCeilingTrip({\n hostRef,\n hostId,\n kind: 'siteMembers',\n ceiling: refusal.ceiling,\n })\n // Opaque to the VISITOR, who is a stranger to this site and not our\n // customer (AGL-1666's rules, restated in the message's own docblock).\n // The one genuinely useful thing to hand them is a door that still\n // opens, so the site's OWN published support address rides along when\n // it has one — read through the host-token registry, whose description\n // of this field is literally \"where visitors should write for help\".\n // Nothing else off the host document may leave the console.\n const contact = HOST_TOKENS['supportEmail']?.resolve(\n hostSnapshot.data() as any,\n )\n return res.status(429).json({\n error: SITE_MEMBER_UNAVAILABLE_MESSAGE,\n // Machine-readable, because the dispatcher's rate limiter answers 429\n // too and the status therefore discriminates nothing.\n code: SITE_MEMBER_CEILING_CODE,\n ...(contact ? { contact } : {}),\n })\n }\n // Sign-ups double as leads for the site owner (AGL-109), through the one\n // writer that enforces `LEADS_MAX_PER_HOST` (AGL-1529). A refused lead\n // never fails the sign-up: the visitor asked for an account, not for a\n // lead record, and the trip is recorded for the owner either way.\n /*\n * THE CAMPAIGN TOUCH, RESOLVED ONCE FOR THE WHOLE SIGN-UP.\n *\n * A sign-up is the identify moment for a visitor who has been anonymous\n * until now, and it writes two records that a campaign can be credited\n * with — the lead and the contact. One resolve, one keyed read, and the\n * two cannot end up naming different campaigns.\n *\n * The MEMBER record itself is not attributed. A member is an account the\n * visitor holds and the lead is the site's record of the same act, so\n * crediting both would count one sign-up twice under two names.\n */\n const signedUpAtMs = Date.now()\n const campaignTouch = await resolveCampaignTouch({\n hostId,\n wire: req.body?.campaignTouch,\n email,\n atMs: signedUpAtMs,\n })\n await addHostLead({\n hostRef,\n hostId,\n lead: {\n email,\n // The name the person just typed (AGL-2303). `campaign-send` reads\n // `leads.name` for merge tags and NOTHING wrote it, so every campaign\n // to the leads audience addressed a blank — with the name sitting one\n // line above, already stored on the member document.\n ...(displayName ? { name: displayName } : {}),\n source: 'signup',\n ...(marketingConsent ? { marketingConsent: true } : {}),\n },\n ...(campaignTouch ? { touch: campaignTouch } : {}),\n })\n // Contacts ingestion (AGL-197).\n void captureHostContact({\n hostId,\n email,\n name: displayName || undefined,\n source: 'member',\n // An account is a subscription to the site, not an enquiry (AGL-2612):\n // the lead this sign-up also files is the sales record, and the stage\n // on the contact says only that the person asked to be kept.\n initialLifecycleStage: 'subscriber',\n interaction: { refId: memberRef.id, summary: 'Joined as a member' },\n ...(marketingConsent ? { marketingConsent: true } : {}),\n ...(campaignTouch ? { campaignTouch } : {}),\n })\n // Event triggers (AGL-128/148): sign-ups double as leads here too. The\n // lead's id is the person key `addHostLead` filed it under, so a webhook\n // can read the row back over `/v1/leads/{leadId}` (AGL-2627); empty for\n // an address that could not be keyed, as every optional payload key is.\n await emitHostEvent(hostId, 'memberSignUp', { email })\n await emitHostEvent(hostId, 'lead', {\n email,\n source: 'signup',\n leadId: personKey(email) ?? '',\n })\n setMemberCookie(res, hostId, mintMemberSession(hostId, memberRef.id))\n return res.status(200).json({ ok: true })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Sign-up failed' })\n }\n}\n"],"names":["memberNameSearchFields","checkVisitorRecordCeiling","HOST_TOKENS","marketingConsentFieldsForHost","personKey","SITE_MEMBER_CEILING_CODE","SITE_MEMBER_UNAVAILABLE_MESSAGE","SITE_MEMBERS_MAX_PER_HOST","addHostLead","firebaseAdmin","recordVisitorRecordCeilingTrip","resolveCampaignTouch","captureHostContact","emitHostEvent","hashMemberPassword","mintMemberSession","setMemberCookie","EMAIL_PATTERN","membershipRegisterHandler","req","res","method","status","json","error","hostId","String","body","email","trim","toLowerCase","password","displayName","slice","marketingConsent","test","length","firestore","app","hostRef","collection","doc","hostSnapshot","get","exists","membersRef","memberRef","refusal","runTransaction","tx","existing","where","limit","empty","duplicate","ceiling","used","count","data","verdict","exceeded","create","passwordScrypt","Date","now","createdAt","FieldValue","serverTimestamp","kind","contact","resolve","code","signedUpAtMs","campaignTouch","wire","atMs","lead","name","source","touch","undefined","initialLifecycleStage","interaction","refId","id","summary","leadId","ok","console"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
* Password reset completion (AGL-552): verifies the recover token
|
|
20
|
+
* (signature + expiry + binding to the member's CURRENT password hash —
|
|
21
|
+
* so a completed reset invalidates every outstanding token) and writes the
|
|
22
|
+
* new scrypt hash. No session is minted here; the client signs in through
|
|
23
|
+
* the normal login route with the new password. Token guessing is a
|
|
24
|
+
* non-issue (256-bit HMAC), so no extra damper beyond login's.
|
|
25
|
+
*/
|
|
26
|
+
export declare const membershipResetHandler: PluginApiHandler;
|
|
@@ -0,0 +1,88 @@
|
|
|
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 { MEMBER_SUSPENDED_ERROR, hashMemberPassword, passwordResetTokenMemberId, verifyPasswordResetToken } from "./membership.js";
|
|
18
|
+
/**
|
|
19
|
+
* Password reset completion (AGL-552): verifies the recover token
|
|
20
|
+
* (signature + expiry + binding to the member's CURRENT password hash —
|
|
21
|
+
* so a completed reset invalidates every outstanding token) and writes the
|
|
22
|
+
* new scrypt hash. No session is minted here; the client signs in through
|
|
23
|
+
* the normal login route with the new password. Token guessing is a
|
|
24
|
+
* non-issue (256-bit HMAC), so no extra damper beyond login's.
|
|
25
|
+
*/ export const membershipResetHandler = async (req, res)=>{
|
|
26
|
+
var _ref, _ref1, _ref2;
|
|
27
|
+
var _req_body, _req_body1, _req_body2;
|
|
28
|
+
if (req.method !== 'POST') {
|
|
29
|
+
return res.status(405).json({
|
|
30
|
+
error: 'Method not allowed'
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const hostId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.hostId) != null ? _ref : '');
|
|
34
|
+
const token = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.token) != null ? _ref1 : '');
|
|
35
|
+
const password = String((_ref2 = (_req_body2 = req.body) == null ? void 0 : _req_body2.password) != null ? _ref2 : '');
|
|
36
|
+
if (!hostId || !token) {
|
|
37
|
+
return res.status(400).json({
|
|
38
|
+
error: 'Invalid request'
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (password.length < 8) {
|
|
42
|
+
return res.status(400).json({
|
|
43
|
+
error: 'Password must be at least 8 characters'
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const invalid = ()=>res.status(400).json({
|
|
47
|
+
error: 'That reset link is invalid or has expired — request a new one.'
|
|
48
|
+
});
|
|
49
|
+
// Structural pre-check only (host + expiry); the signature binds to the
|
|
50
|
+
// member's current hash, which we have to load first.
|
|
51
|
+
const memberId = passwordResetTokenMemberId(hostId, token);
|
|
52
|
+
if (!memberId) return invalid();
|
|
53
|
+
try {
|
|
54
|
+
const firestore = firebaseAdmin.app().firestore();
|
|
55
|
+
const memberRef = firestore.collection('hosts').doc(hostId).collection('siteMembers').doc(memberId);
|
|
56
|
+
const memberDoc = await memberRef.get();
|
|
57
|
+
if (!memberDoc.exists) return invalid();
|
|
58
|
+
if (!verifyPasswordResetToken(hostId, token, memberDoc.get('passwordScrypt'))) {
|
|
59
|
+
return invalid();
|
|
60
|
+
}
|
|
61
|
+
// Suspension gate (AGL-546/550): a token minted before the
|
|
62
|
+
// suspension stays cryptographically valid for its hour, so reject
|
|
63
|
+
// here — recovery must not rehabilitate a suspended account. Checked
|
|
64
|
+
// AFTER the signature so only the mailed-link holder (who already
|
|
65
|
+
// knows the account exists) can observe the suspension.
|
|
66
|
+
if (memberDoc.get('suspended') === true) {
|
|
67
|
+
return res.status(403).json({
|
|
68
|
+
error: MEMBER_SUSPENDED_ERROR
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
await memberRef.set({
|
|
72
|
+
passwordScrypt: hashMemberPassword(password),
|
|
73
|
+
passwordResetAt: firebaseAdmin.firestore.FieldValue.serverTimestamp()
|
|
74
|
+
}, {
|
|
75
|
+
merge: true
|
|
76
|
+
});
|
|
77
|
+
return res.status(200).json({
|
|
78
|
+
ok: true
|
|
79
|
+
});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error(error);
|
|
82
|
+
return res.status(500).json({
|
|
83
|
+
error: 'Reset failed'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=membership-reset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/membership-reset.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 {\n MEMBER_SUSPENDED_ERROR,\n hashMemberPassword,\n passwordResetTokenMemberId,\n verifyPasswordResetToken,\n} from './membership'\n\n/**\n * Password reset completion (AGL-552): verifies the recover token\n * (signature + expiry + binding to the member's CURRENT password hash —\n * so a completed reset invalidates every outstanding token) and writes the\n * new scrypt hash. No session is minted here; the client signs in through\n * the normal login route with the new password. Token guessing is a\n * non-issue (256-bit HMAC), so no extra damper beyond login's.\n */\nexport const membershipResetHandler: 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 token = String(req.body?.token ?? '')\n const password = String(req.body?.password ?? '')\n if (!hostId || !token) {\n return res.status(400).json({ error: 'Invalid request' })\n }\n if (password.length < 8) {\n return res\n .status(400)\n .json({ error: 'Password must be at least 8 characters' })\n }\n const invalid = () =>\n res.status(400).json({\n error: 'That reset link is invalid or has expired — request a new one.',\n })\n // Structural pre-check only (host + expiry); the signature binds to the\n // member's current hash, which we have to load first.\n const memberId = passwordResetTokenMemberId(hostId, token)\n if (!memberId) return invalid()\n try {\n const firestore = firebaseAdmin.app().firestore()\n const memberRef = firestore\n .collection('hosts')\n .doc(hostId)\n .collection('siteMembers')\n .doc(memberId)\n const memberDoc = await memberRef.get()\n if (!memberDoc.exists) return invalid()\n if (\n !verifyPasswordResetToken(hostId, token, memberDoc.get('passwordScrypt'))\n ) {\n return invalid()\n }\n // Suspension gate (AGL-546/550): a token minted before the\n // suspension stays cryptographically valid for its hour, so reject\n // here — recovery must not rehabilitate a suspended account. Checked\n // AFTER the signature so only the mailed-link holder (who already\n // knows the account exists) can observe the suspension.\n if (memberDoc.get('suspended') === true) {\n return res.status(403).json({ error: MEMBER_SUSPENDED_ERROR })\n }\n await memberRef.set(\n {\n passwordScrypt: hashMemberPassword(password),\n passwordResetAt: firebaseAdmin.firestore.FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n return res.status(200).json({ ok: true })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Reset failed' })\n }\n}\n"],"names":["firebaseAdmin","MEMBER_SUSPENDED_ERROR","hashMemberPassword","passwordResetTokenMemberId","verifyPasswordResetToken","membershipResetHandler","req","res","method","status","json","error","hostId","String","body","token","password","length","invalid","memberId","firestore","app","memberRef","collection","doc","memberDoc","get","exists","set","passwordScrypt","passwordResetAt","FieldValue","serverTimestamp","merge","ok","console"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,aAAa,QAAQ,2BAA0B;AACxD,SACEC,sBAAsB,EACtBC,kBAAkB,EAClBC,0BAA0B,EAC1BC,wBAAwB,QACnB,kBAAc;AAErB;;;;;;;CAOC,GACD,OAAO,MAAMC,yBAA2C,OAAOC,KAAKC;;QAI5CD,WACDA,YACGA;IALxB,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,QAAQF,iBAAOP,aAAAA,IAAIQ,IAAI,qBAARR,WAAUS,KAAK,oBAAI;IACxC,MAAMC,WAAWH,iBAAOP,aAAAA,IAAIQ,IAAI,qBAARR,WAAUU,QAAQ,oBAAI;IAC9C,IAAI,CAACJ,UAAU,CAACG,OAAO;QACrB,OAAOR,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAkB;IACzD;IACA,IAAIK,SAASC,MAAM,GAAG,GAAG;QACvB,OAAOV,IACJE,MAAM,CAAC,KACPC,IAAI,CAAC;YAAEC,OAAO;QAAyC;IAC5D;IACA,MAAMO,UAAU,IACdX,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YACnBC,OAAO;QACT;IACF,wEAAwE;IACxE,sDAAsD;IACtD,MAAMQ,WAAWhB,2BAA2BS,QAAQG;IACpD,IAAI,CAACI,UAAU,OAAOD;IACtB,IAAI;QACF,MAAME,YAAYpB,cAAcqB,GAAG,GAAGD,SAAS;QAC/C,MAAME,YAAYF,UACfG,UAAU,CAAC,SACXC,GAAG,CAACZ,QACJW,UAAU,CAAC,eACXC,GAAG,CAACL;QACP,MAAMM,YAAY,MAAMH,UAAUI,GAAG;QACrC,IAAI,CAACD,UAAUE,MAAM,EAAE,OAAOT;QAC9B,IACE,CAACd,yBAAyBQ,QAAQG,OAAOU,UAAUC,GAAG,CAAC,oBACvD;YACA,OAAOR;QACT;QACA,2DAA2D;QAC3D,mEAAmE;QACnE,qEAAqE;QACrE,kEAAkE;QAClE,wDAAwD;QACxD,IAAIO,UAAUC,GAAG,CAAC,iBAAiB,MAAM;YACvC,OAAOnB,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;gBAAEC,OAAOV;YAAuB;QAC9D;QACA,MAAMqB,UAAUM,GAAG,CACjB;YACEC,gBAAgB3B,mBAAmBc;YACnCc,iBAAiB9B,cAAcoB,SAAS,CAACW,UAAU,CAACC,eAAe;QACrE,GACA;YAAEC,OAAO;QAAK;QAEhB,OAAO1B,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEwB,IAAI;QAAK;IACzC,EAAE,OAAOvB,OAAO;QACdwB,QAAQxB,KAAK,CAACA;QACd,OAAOJ,IAAIE,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAe;IACtD;AACF,EAAC"}
|
|
@@ -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 wishlist (AGL-297): product ids on the siteMembers doc.
|
|
20
|
+
* Guests keep theirs in localStorage; the wishlist block merges the
|
|
21
|
+
* local list into the member's on first signed-in read.
|
|
22
|
+
*/
|
|
23
|
+
export declare const membershipWishlistHandler: PluginApiHandler;
|
|
@@ -0,0 +1,89 @@
|
|
|
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
|
+
/**
|
|
19
|
+
* Member wishlist (AGL-297): product ids on the siteMembers doc.
|
|
20
|
+
* Guests keep theirs in localStorage; the wishlist block merges the
|
|
21
|
+
* local list into the member's on first signed-in read.
|
|
22
|
+
*/ export const membershipWishlistHandler = async (req, res)=>{
|
|
23
|
+
var _ref;
|
|
24
|
+
var _req_body;
|
|
25
|
+
const isPost = req.method === 'POST';
|
|
26
|
+
if (!isPost && req.method !== 'GET') {
|
|
27
|
+
return res.status(405).json({
|
|
28
|
+
error: 'Method not allowed'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const hostId = String((_ref = isPost ? (_req_body = req.body) == null ? void 0 : _req_body.hostId : req.query.hostId) != null ? _ref : '');
|
|
32
|
+
if (!hostId) return res.status(400).json({
|
|
33
|
+
error: 'Missing hostId'
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
var _auth_member_get;
|
|
37
|
+
// Suspension gate (AGL-550): loads the member doc too, so a
|
|
38
|
+
// suspended member's cookie (AGL-546) is 403'd and cleared here.
|
|
39
|
+
const auth = await requireActiveMember(req, res, hostId);
|
|
40
|
+
if (!auth) return;
|
|
41
|
+
const memberRef = firebaseAdmin.app().firestore().collection('hosts').doc(hostId).collection('siteMembers').doc(auth.memberId);
|
|
42
|
+
let wishlist = (_auth_member_get = auth.member.get('wishlist')) != null ? _auth_member_get : [];
|
|
43
|
+
if (isPost) {
|
|
44
|
+
var _ref1;
|
|
45
|
+
var _req_body1;
|
|
46
|
+
const action = String((_ref1 = (_req_body1 = req.body) == null ? void 0 : _req_body1.action) != null ? _ref1 : 'add');
|
|
47
|
+
if (action === 'merge') {
|
|
48
|
+
var _req_body2;
|
|
49
|
+
const incoming = Array.isArray((_req_body2 = req.body) == null ? void 0 : _req_body2.productIds) ? req.body.productIds.map(String).slice(0, 100) : [];
|
|
50
|
+
wishlist = [
|
|
51
|
+
...new Set([
|
|
52
|
+
...wishlist,
|
|
53
|
+
...incoming
|
|
54
|
+
])
|
|
55
|
+
].slice(0, 200);
|
|
56
|
+
} else {
|
|
57
|
+
var _ref2;
|
|
58
|
+
var _req_body3;
|
|
59
|
+
const productId = String((_ref2 = (_req_body3 = req.body) == null ? void 0 : _req_body3.productId) != null ? _ref2 : '');
|
|
60
|
+
if (!productId) {
|
|
61
|
+
return res.status(400).json({
|
|
62
|
+
error: 'Missing productId'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
wishlist = action === 'remove' ? wishlist.filter((id)=>id !== productId) : [
|
|
66
|
+
...new Set([
|
|
67
|
+
...wishlist,
|
|
68
|
+
productId
|
|
69
|
+
])
|
|
70
|
+
].slice(0, 200);
|
|
71
|
+
}
|
|
72
|
+
await memberRef.set({
|
|
73
|
+
wishlist
|
|
74
|
+
}, {
|
|
75
|
+
merge: true
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return res.status(200).json({
|
|
79
|
+
productIds: wishlist
|
|
80
|
+
});
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error(error);
|
|
83
|
+
return res.status(500).json({
|
|
84
|
+
error: 'Wishlist unavailable'
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=membership-wishlist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/membership-wishlist.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'\n\n/**\n * Member wishlist (AGL-297): product ids on the siteMembers doc.\n * Guests keep theirs in localStorage; the wishlist block merges the\n * local list into the member's on first signed-in read.\n */\nexport const membershipWishlistHandler: PluginApiHandler = async (req, res) => {\n const isPost = req.method === 'POST'\n if (!isPost && req.method !== 'GET') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const hostId = String(\n (isPost ? req.body?.hostId : req.query.hostId) ?? '',\n )\n if (!hostId) return res.status(400).json({ error: 'Missing hostId' })\n\n try {\n // Suspension gate (AGL-550): loads the member doc too, so a\n // suspended member's cookie (AGL-546) is 403'd and cleared here.\n const auth = await requireActiveMember(req, res, hostId)\n if (!auth) return\n const memberRef = firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('siteMembers')\n .doc(auth.memberId)\n let wishlist: string[] = (auth.member.get('wishlist') ?? []) as string[]\n\n if (isPost) {\n const action = String(req.body?.action ?? 'add')\n if (action === 'merge') {\n const incoming = Array.isArray(req.body?.productIds)\n ? (req.body.productIds as string[]).map(String).slice(0, 100)\n : []\n wishlist = [...new Set([...wishlist, ...incoming])].slice(0, 200)\n } else {\n const productId = String(req.body?.productId ?? '')\n if (!productId) {\n return res.status(400).json({ error: 'Missing productId' })\n }\n wishlist =\n action === 'remove'\n ? wishlist.filter((id) => id !== productId)\n : [...new Set([...wishlist, productId])].slice(0, 200)\n }\n await memberRef.set({ wishlist }, { merge: true })\n }\n return res.status(200).json({ productIds: wishlist })\n } catch (error) {\n console.error(error)\n return res.status(500).json({ error: 'Wishlist unavailable' })\n }\n}\n"],"names":["firebaseAdmin","requireActiveMember","membershipWishlistHandler","req","res","isPost","method","status","json","error","hostId","String","body","query","auth","memberRef","app","firestore","collection","doc","memberId","wishlist","member","get","action","incoming","Array","isArray","productIds","map","slice","Set","productId","filter","id","set","merge","console"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,aAAa,QAAQ,2BAA0B;AACxD,SAASC,mBAAmB,QAAQ,kBAAc;AAElD;;;;CAIC,GACD,OAAO,MAAMC,4BAA8C,OAAOC,KAAKC;QAMlEC;QAASF;IALZ,MAAME,SAASF,IAAIG,MAAM,KAAK;IAC9B,IAAI,CAACD,UAAUF,IAAIG,MAAM,KAAK,OAAO;QACnC,OAAOF,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAqB;IAC5D;IACA,MAAMC,SAASC,QACZN,OAAAA,UAASF,YAAAA,IAAIS,IAAI,qBAART,UAAUO,MAAM,GAAGP,IAAIU,KAAK,CAACH,MAAM,YAA5CL,OAAiD;IAEpD,IAAI,CAACK,QAAQ,OAAON,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;QAAEC,OAAO;IAAiB;IAEnE,IAAI;YAYwBK;QAX1B,4DAA4D;QAC5D,iEAAiE;QACjE,MAAMA,OAAO,MAAMb,oBAAoBE,KAAKC,KAAKM;QACjD,IAAI,CAACI,MAAM;QACX,MAAMC,YAAYf,cACfgB,GAAG,GACHC,SAAS,GACTC,UAAU,CAAC,SACXC,GAAG,CAACT,QACJQ,UAAU,CAAC,eACXC,GAAG,CAACL,KAAKM,QAAQ;QACpB,IAAIC,YAAsBP,mBAAAA,KAAKQ,MAAM,CAACC,GAAG,CAAC,uBAAhBT,mBAA+B,EAAE;QAE3D,IAAIT,QAAQ;;gBACYF;YAAtB,MAAMqB,SAASb,iBAAOR,aAAAA,IAAIS,IAAI,qBAART,WAAUqB,MAAM,oBAAI;YAC1C,IAAIA,WAAW,SAAS;oBACSrB;gBAA/B,MAAMsB,WAAWC,MAAMC,OAAO,EAACxB,aAAAA,IAAIS,IAAI,qBAART,WAAUyB,UAAU,IAC/C,AAACzB,IAAIS,IAAI,CAACgB,UAAU,CAAcC,GAAG,CAAClB,QAAQmB,KAAK,CAAC,GAAG,OACvD,EAAE;gBACNT,WAAW;uBAAI,IAAIU,IAAI;2BAAIV;2BAAaI;qBAAS;iBAAE,CAACK,KAAK,CAAC,GAAG;YAC/D,OAAO;;oBACoB3B;gBAAzB,MAAM6B,YAAYrB,iBAAOR,aAAAA,IAAIS,IAAI,qBAART,WAAU6B,SAAS,oBAAI;gBAChD,IAAI,CAACA,WAAW;oBACd,OAAO5B,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;wBAAEC,OAAO;oBAAoB;gBAC3D;gBACAY,WACEG,WAAW,WACPH,SAASY,MAAM,CAAC,CAACC,KAAOA,OAAOF,aAC/B;uBAAI,IAAID,IAAI;2BAAIV;wBAAUW;qBAAU;iBAAE,CAACF,KAAK,CAAC,GAAG;YACxD;YACA,MAAMf,UAAUoB,GAAG,CAAC;gBAAEd;YAAS,GAAG;gBAAEe,OAAO;YAAK;QAClD;QACA,OAAOhC,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEoB,YAAYP;QAAS;IACrD,EAAE,OAAOZ,OAAO;QACd4B,QAAQ5B,KAAK,CAACA;QACd,OAAOL,IAAIG,MAAM,CAAC,KAAKC,IAAI,CAAC;YAAEC,OAAO;QAAuB;IAC9D;AACF,EAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
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 { PluginApiRequest, PluginApiResponse } from '@aglyn/aglyn/server';
|
|
18
|
+
export declare function hashMemberPassword(password: string): string;
|
|
19
|
+
export declare function verifyMemberPassword(password: string, stored: string | undefined): boolean;
|
|
20
|
+
export declare function mintMemberSession(hostId: string, memberId: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Verified session contents. `issuedAtMs` is derived, not stored: the
|
|
23
|
+
* payload carries `now + TTL`, so subtracting the TTL recovers the mint
|
|
24
|
+
* time. That is what lets sessions be revoked in bulk (AGL-914) without
|
|
25
|
+
* changing the token format or invalidating cookies already in the wild.
|
|
26
|
+
*/
|
|
27
|
+
export interface MemberSessionToken {
|
|
28
|
+
memberId: string;
|
|
29
|
+
issuedAtMs: number;
|
|
30
|
+
}
|
|
31
|
+
/** Returns the session contents when the cookie verifies for this host. */
|
|
32
|
+
export declare function parseMemberSession(hostId: string, token: string | undefined): MemberSessionToken | null;
|
|
33
|
+
/** Returns the memberId when the cookie verifies for this host. */
|
|
34
|
+
export declare function verifyMemberSession(hostId: string, token: string | undefined): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Single-use, time-boxed password-reset token (AGL-552):
|
|
37
|
+
* `hostId.memberId.expiry.HMAC(payload + password-fingerprint)`. Same
|
|
38
|
+
* secret as sessions; the fingerprint binding is what makes it one-shot.
|
|
39
|
+
*/
|
|
40
|
+
export declare function mintPasswordResetToken(hostId: string, memberId: string, passwordScrypt: string | undefined): string;
|
|
41
|
+
/**
|
|
42
|
+
* First step of reset verification: extract the memberId from a
|
|
43
|
+
* well-formed, unexpired token for THIS host. The signature is NOT checked
|
|
44
|
+
* here (it binds to the member's current password hash, which the caller
|
|
45
|
+
* has to load first) — never trust the result without a follow-up
|
|
46
|
+
* {@link verifyPasswordResetToken}.
|
|
47
|
+
*/
|
|
48
|
+
export declare function passwordResetTokenMemberId(hostId: string, token: string | undefined): string | null;
|
|
49
|
+
/** Full reset-token check: host, expiry, and password-hash binding. */
|
|
50
|
+
export declare function verifyPasswordResetToken(hostId: string, token: string | undefined, currentPasswordScrypt: string | undefined): boolean;
|
|
51
|
+
export declare function memberCookieName(hostId: string): string;
|
|
52
|
+
export declare function setMemberCookie(res: PluginApiResponse, hostId: string, token: string | null): void;
|
|
53
|
+
export declare function readMemberSession(req: PluginApiRequest, hostId: string): string | null;
|
|
54
|
+
/**
|
|
55
|
+
* Field name for the bulk session cut-off (AGL-914). Set to `Date.now()`
|
|
56
|
+
* whenever every existing session for a member must stop working — today
|
|
57
|
+
* only when an admin sets their password from the console, since a rewritten
|
|
58
|
+
* `passwordScrypt` does NOT invalidate sessions on its own (unlike a reset
|
|
59
|
+
* TOKEN, which binds to the hash by design).
|
|
60
|
+
*/
|
|
61
|
+
export declare const MEMBER_SESSIONS_VALID_FROM_FIELD = "sessionsValidFromMs";
|
|
62
|
+
/** Suspension rejection body — matches the login flow's AGL-546 message. */
|
|
63
|
+
export declare const MEMBER_SUSPENDED_ERROR = "This account has been suspended. Contact the site owner.";
|
|
64
|
+
/** Resolved member-session state, see {@link readActiveMemberSession}. */
|
|
65
|
+
export type MemberSessionCheck = {
|
|
66
|
+
status: 'anonymous';
|
|
67
|
+
} | {
|
|
68
|
+
status: 'suspended';
|
|
69
|
+
memberId: string;
|
|
70
|
+
} | {
|
|
71
|
+
status: 'active';
|
|
72
|
+
memberId: string;
|
|
73
|
+
member: FirebaseFirestore.DocumentSnapshot;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Session + member-doc resolution for cookie-authenticated endpoints
|
|
77
|
+
* (AGL-550). A verified cookie alone stopped being enough once console
|
|
78
|
+
* suspension (AGL-546) landed: the HMAC session outlives the member's
|
|
79
|
+
* standing, so every endpoint must also load the doc and check
|
|
80
|
+
* `suspended`. That is one doc read per request — acceptable, and most
|
|
81
|
+
* endpoints needed the member doc anyway. A deleted member doc reads as
|
|
82
|
+
* `anonymous` (same as no cookie).
|
|
83
|
+
*/
|
|
84
|
+
export declare function readActiveMemberSession(req: PluginApiRequest, hostId: string): Promise<MemberSessionCheck>;
|
|
85
|
+
/**
|
|
86
|
+
* Guard for cookie-authenticated member endpoints (AGL-550): resolves
|
|
87
|
+
* the session AND the member doc, writing the rejection itself — 401
|
|
88
|
+
* (`signInError`) when there is no usable session, 403 with the session
|
|
89
|
+
* cookie cleared (mirroring membership-account's AGL-546 handling, so
|
|
90
|
+
* the site stops presenting a signed-in shell) when the member is
|
|
91
|
+
* suspended. Returns null after responding; handlers `if (!auth) return`.
|
|
92
|
+
*/
|
|
93
|
+
export declare function requireActiveMember(req: PluginApiRequest, res: PluginApiResponse, hostId: string, signInError?: string): Promise<{
|
|
94
|
+
memberId: string;
|
|
95
|
+
member: FirebaseFirestore.DocumentSnapshot;
|
|
96
|
+
} | null>;
|