@aglyn/tenant-data-admin 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 +7 -0
- package/jest.integration.config.d.ts +39 -0
- package/jest.integration.setup.d.ts +17 -0
- package/package.json +51 -0
- package/src/index.d.ts +17 -0
- package/src/index.js +18 -0
- package/src/index.js.map +1 -0
- package/src/lib/render-cache.d.ts +184 -0
- package/src/lib/render-cache.js +198 -0
- package/src/lib/render-cache.js.map +1 -0
- package/src/lib/server/account-addresses.d.ts +160 -0
- package/src/lib/server/account-addresses.js +311 -0
- package/src/lib/server/account-addresses.js.map +1 -0
- package/src/lib/server/account-emails.d.ts +219 -0
- package/src/lib/server/account-emails.js +640 -0
- package/src/lib/server/account-emails.js.map +1 -0
- package/src/lib/server/admin-audit.d.ts +180 -0
- package/src/lib/server/admin-audit.js +233 -0
- package/src/lib/server/admin-audit.js.map +1 -0
- package/src/lib/server/analytics-retention.d.ts +63 -0
- package/src/lib/server/analytics-retention.js +66 -0
- package/src/lib/server/analytics-retention.js.map +1 -0
- package/src/lib/server/api-http.d.ts +159 -0
- package/src/lib/server/api-http.js +230 -0
- package/src/lib/server/api-http.js.map +1 -0
- package/src/lib/server/api-keys.d.ts +117 -0
- package/src/lib/server/api-keys.js +298 -0
- package/src/lib/server/api-keys.js.map +1 -0
- package/src/lib/server/auth-handoff.d.ts +165 -0
- package/src/lib/server/auth-handoff.js +482 -0
- package/src/lib/server/auth-handoff.js.map +1 -0
- package/src/lib/server/auth-pools.d.ts +246 -0
- package/src/lib/server/auth-pools.js +551 -0
- package/src/lib/server/auth-pools.js.map +1 -0
- package/src/lib/server/billing-addons.d.ts +171 -0
- package/src/lib/server/billing-addons.js +277 -0
- package/src/lib/server/billing-addons.js.map +1 -0
- package/src/lib/server/campaign-attribution-store.d.ts +55 -0
- package/src/lib/server/campaign-attribution-store.js +95 -0
- package/src/lib/server/campaign-attribution-store.js.map +1 -0
- package/src/lib/server/campaign-conversion-attribution.d.ts +205 -0
- package/src/lib/server/campaign-conversion-attribution.js +279 -0
- package/src/lib/server/campaign-conversion-attribution.js.map +1 -0
- package/src/lib/server/client-error-report.d.ts +301 -0
- package/src/lib/server/client-error-report.js +755 -0
- package/src/lib/server/client-error-report.js.map +1 -0
- package/src/lib/server/connect-account-status.d.ts +74 -0
- package/src/lib/server/connect-account-status.js +94 -0
- package/src/lib/server/connect-account-status.js.map +1 -0
- package/src/lib/server/connect-payout-failure.d.ts +103 -0
- package/src/lib/server/connect-payout-failure.js +127 -0
- package/src/lib/server/connect-payout-failure.js.map +1 -0
- package/src/lib/server/console-api-rate-limit.d.ts +81 -0
- package/src/lib/server/console-api-rate-limit.js +111 -0
- package/src/lib/server/console-api-rate-limit.js.map +1 -0
- package/src/lib/server/console-domains.d.ts +326 -0
- package/src/lib/server/console-domains.js +779 -0
- package/src/lib/server/console-domains.js.map +1 -0
- package/src/lib/server/consume-once.d.ts +91 -0
- package/src/lib/server/consume-once.js +96 -0
- package/src/lib/server/consume-once.js.map +1 -0
- package/src/lib/server/contact-company-link.d.ts +85 -0
- package/src/lib/server/contact-company-link.js +105 -0
- package/src/lib/server/contact-company-link.js.map +1 -0
- package/src/lib/server/contact-email-engagement.d.ts +32 -0
- package/src/lib/server/contact-email-engagement.js +145 -0
- package/src/lib/server/contact-email-engagement.js.map +1 -0
- package/src/lib/server/contact-email-index.d.ts +56 -0
- package/src/lib/server/contact-email-index.js +158 -0
- package/src/lib/server/contact-email-index.js.map +1 -0
- package/src/lib/server/contact-lifecycle-floor.d.ts +89 -0
- package/src/lib/server/contact-lifecycle-floor.js +88 -0
- package/src/lib/server/contact-lifecycle-floor.js.map +1 -0
- package/src/lib/server/contact-merge.d.ts +55 -0
- package/src/lib/server/contact-merge.js +279 -0
- package/src/lib/server/contact-merge.js.map +1 -0
- package/src/lib/server/contact-suppression.d.ts +153 -0
- package/src/lib/server/contact-suppression.js +259 -0
- package/src/lib/server/contact-suppression.js.map +1 -0
- package/src/lib/server/crm-booking-activity.d.ts +86 -0
- package/src/lib/server/crm-booking-activity.js +251 -0
- package/src/lib/server/crm-booking-activity.js.map +1 -0
- package/src/lib/server/crm-email-activity.d.ts +112 -0
- package/src/lib/server/crm-email-activity.js +143 -0
- package/src/lib/server/crm-email-activity.js.map +1 -0
- package/src/lib/server/crm-inbound-email.d.ts +145 -0
- package/src/lib/server/crm-inbound-email.js +287 -0
- package/src/lib/server/crm-inbound-email.js.map +1 -0
- package/src/lib/server/crm-next-activity.d.ts +74 -0
- package/src/lib/server/crm-next-activity.js +194 -0
- package/src/lib/server/crm-next-activity.js.map +1 -0
- package/src/lib/server/crm-records.d.ts +144 -0
- package/src/lib/server/crm-records.js +218 -0
- package/src/lib/server/crm-records.js.map +1 -0
- package/src/lib/server/csp-aggregate.d.ts +62 -0
- package/src/lib/server/csp-aggregate.js +276 -0
- package/src/lib/server/csp-aggregate.js.map +1 -0
- package/src/lib/server/data-storage-gate.d.ts +66 -0
- package/src/lib/server/data-storage-gate.js +48 -0
- package/src/lib/server/data-storage-gate.js.map +1 -0
- package/src/lib/server/dataset-live-pages.d.ts +179 -0
- package/src/lib/server/dataset-live-pages.js +421 -0
- package/src/lib/server/dataset-live-pages.js.map +1 -0
- package/src/lib/server/dns-probe.d.ts +53 -0
- package/src/lib/server/dns-probe.js +184 -0
- package/src/lib/server/dns-probe.js.map +1 -0
- package/src/lib/server/document-id.d.ts +29 -0
- package/src/lib/server/document-id.js +100 -0
- package/src/lib/server/document-id.js.map +1 -0
- package/src/lib/server/domain-provider-vercel.d.ts +33 -0
- package/src/lib/server/domain-provider-vercel.js +298 -0
- package/src/lib/server/domain-provider-vercel.js.map +1 -0
- package/src/lib/server/domain-provider-webhook.d.ts +73 -0
- package/src/lib/server/domain-provider-webhook.js +223 -0
- package/src/lib/server/domain-provider-webhook.js.map +1 -0
- package/src/lib/server/domain-provider-wildcard.d.ts +54 -0
- package/src/lib/server/domain-provider-wildcard.js +163 -0
- package/src/lib/server/domain-provider-wildcard.js.map +1 -0
- package/src/lib/server/domain-provider.d.ts +197 -0
- package/src/lib/server/domain-provider.js +221 -0
- package/src/lib/server/domain-provider.js.map +1 -0
- package/src/lib/server/duplicate-activity.d.ts +54 -0
- package/src/lib/server/duplicate-activity.js +67 -0
- package/src/lib/server/duplicate-activity.js.map +1 -0
- package/src/lib/server/duplicate-resource.d.ts +55 -0
- package/src/lib/server/duplicate-resource.js +474 -0
- package/src/lib/server/duplicate-resource.js.map +1 -0
- package/src/lib/server/dynamic-list-materialize.d.ts +153 -0
- package/src/lib/server/dynamic-list-materialize.js +581 -0
- package/src/lib/server/dynamic-list-materialize.js.map +1 -0
- package/src/lib/server/edit-access-authz.d.ts +56 -0
- package/src/lib/server/edit-access-authz.js +107 -0
- package/src/lib/server/edit-access-authz.js.map +1 -0
- package/src/lib/server/edit-access-token.d.ts +72 -0
- package/src/lib/server/edit-access-token.js +116 -0
- package/src/lib/server/edit-access-token.js.map +1 -0
- package/src/lib/server/edit-hint-token.d.ts +83 -0
- package/src/lib/server/edit-hint-token.js +119 -0
- package/src/lib/server/edit-hint-token.js.map +1 -0
- package/src/lib/server/email-delivery-log.d.ts +509 -0
- package/src/lib/server/email-delivery-log.js +1043 -0
- package/src/lib/server/email-delivery-log.js.map +1 -0
- package/src/lib/server/email-flow-gate.d.ts +80 -0
- package/src/lib/server/email-flow-gate.js +153 -0
- package/src/lib/server/email-flow-gate.js.map +1 -0
- package/src/lib/server/email-marketing-gate.d.ts +333 -0
- package/src/lib/server/email-marketing-gate.js +639 -0
- package/src/lib/server/email-marketing-gate.js.map +1 -0
- package/src/lib/server/email-metering.d.ts +293 -0
- package/src/lib/server/email-metering.js +359 -0
- package/src/lib/server/email-metering.js.map +1 -0
- package/src/lib/server/email-revenue-attribution.d.ts +185 -0
- package/src/lib/server/email-revenue-attribution.js +300 -0
- package/src/lib/server/email-revenue-attribution.js.map +1 -0
- package/src/lib/server/email-send-rate.d.ts +232 -0
- package/src/lib/server/email-send-rate.js +417 -0
- package/src/lib/server/email-send-rate.js.map +1 -0
- package/src/lib/server/email-sender-reputation.d.ts +317 -0
- package/src/lib/server/email-sender-reputation.js +464 -0
- package/src/lib/server/email-sender-reputation.js.map +1 -0
- package/src/lib/server/email-suppression.d.ts +490 -0
- package/src/lib/server/email-suppression.js +648 -0
- package/src/lib/server/email-suppression.js.map +1 -0
- package/src/lib/server/email-topic-confirmation.d.ts +150 -0
- package/src/lib/server/email-topic-confirmation.js +272 -0
- package/src/lib/server/email-topic-confirmation.js.map +1 -0
- package/src/lib/server/email-unsubscribe-link.d.ts +174 -0
- package/src/lib/server/email-unsubscribe-link.js +236 -0
- package/src/lib/server/email-unsubscribe-link.js.map +1 -0
- package/src/lib/server/erase-person.d.ts +116 -0
- package/src/lib/server/erase-person.js +272 -0
- package/src/lib/server/erase-person.js.map +1 -0
- package/src/lib/server/erase.d.ts +458 -0
- package/src/lib/server/erase.js +1506 -0
- package/src/lib/server/erase.js.map +1 -0
- package/src/lib/server/firebase-admin.d.ts +71 -0
- package/src/lib/server/firebase-admin.js +343 -0
- package/src/lib/server/firebase-admin.js.map +1 -0
- package/src/lib/server/form-dataset-binding-token.d.ts +35 -0
- package/src/lib/server/form-dataset-binding-token.js +107 -0
- package/src/lib/server/form-dataset-binding-token.js.map +1 -0
- package/src/lib/server/free-workspace-cap.d.ts +201 -0
- package/src/lib/server/free-workspace-cap.js +404 -0
- package/src/lib/server/free-workspace-cap.js.map +1 -0
- package/src/lib/server/ga4-measurement-protocol.d.ts +256 -0
- package/src/lib/server/ga4-measurement-protocol.js +558 -0
- package/src/lib/server/ga4-measurement-protocol.js.map +1 -0
- package/src/lib/server/host-email-tokens.d.ts +47 -0
- package/src/lib/server/host-email-tokens.js +72 -0
- package/src/lib/server/host-email-tokens.js.map +1 -0
- package/src/lib/server/host-memberships.d.ts +54 -0
- package/src/lib/server/host-memberships.js +194 -0
- package/src/lib/server/host-memberships.js.map +1 -0
- package/src/lib/server/host-sending-domain.d.ts +292 -0
- package/src/lib/server/host-sending-domain.js +451 -0
- package/src/lib/server/host-sending-domain.js.map +1 -0
- package/src/lib/server/host-visitor-records.d.ts +150 -0
- package/src/lib/server/host-visitor-records.js +262 -0
- package/src/lib/server/host-visitor-records.js.map +1 -0
- package/src/lib/server/id-token-refusal.d.ts +28 -0
- package/src/lib/server/id-token-refusal.js +111 -0
- package/src/lib/server/id-token-refusal.js.map +1 -0
- package/src/lib/server/legal-acceptance.d.ts +214 -0
- package/src/lib/server/legal-acceptance.js +305 -0
- package/src/lib/server/legal-acceptance.js.map +1 -0
- package/src/lib/server/list-members.d.ts +211 -0
- package/src/lib/server/list-members.js +176 -0
- package/src/lib/server/list-members.js.map +1 -0
- package/src/lib/server/live-page-usage.d.ts +202 -0
- package/src/lib/server/live-page-usage.js +300 -0
- package/src/lib/server/live-page-usage.js.map +1 -0
- package/src/lib/server/lockdown.d.ts +313 -0
- package/src/lib/server/lockdown.js +643 -0
- package/src/lib/server/lockdown.js.map +1 -0
- package/src/lib/server/media-cdn-rate-limit.d.ts +188 -0
- package/src/lib/server/media-cdn-rate-limit.js +204 -0
- package/src/lib/server/media-cdn-rate-limit.js.map +1 -0
- package/src/lib/server/media-delivery.d.ts +267 -0
- package/src/lib/server/media-delivery.js +485 -0
- package/src/lib/server/media-delivery.js.map +1 -0
- package/src/lib/server/media-download-tokens.d.ts +161 -0
- package/src/lib/server/media-download-tokens.js +386 -0
- package/src/lib/server/media-download-tokens.js.map +1 -0
- package/src/lib/server/media-quarantine.d.ts +127 -0
- package/src/lib/server/media-quarantine.js +170 -0
- package/src/lib/server/media-quarantine.js.map +1 -0
- package/src/lib/server/media-signing.d.ts +126 -0
- package/src/lib/server/media-signing.js +159 -0
- package/src/lib/server/media-signing.js.map +1 -0
- package/src/lib/server/media-storage-path.d.ts +96 -0
- package/src/lib/server/media-storage-path.js +111 -0
- package/src/lib/server/media-storage-path.js.map +1 -0
- package/src/lib/server/media-strong-digest.d.ts +85 -0
- package/src/lib/server/media-strong-digest.js +190 -0
- package/src/lib/server/media-strong-digest.js.map +1 -0
- package/src/lib/server/media-tombstone.d.ts +276 -0
- package/src/lib/server/media-tombstone.js +421 -0
- package/src/lib/server/media-tombstone.js.map +1 -0
- package/src/lib/server/media-variants.d.ts +309 -0
- package/src/lib/server/media-variants.js +428 -0
- package/src/lib/server/media-variants.js.map +1 -0
- package/src/lib/server/member-email-aliases.d.ts +180 -0
- package/src/lib/server/member-email-aliases.js +420 -0
- package/src/lib/server/member-email-aliases.js.map +1 -0
- package/src/lib/server/member-photo.d.ts +88 -0
- package/src/lib/server/member-photo.js +185 -0
- package/src/lib/server/member-photo.js.map +1 -0
- package/src/lib/server/membership-recover-throttle.d.ts +211 -0
- package/src/lib/server/membership-recover-throttle.js +231 -0
- package/src/lib/server/membership-recover-throttle.js.map +1 -0
- package/src/lib/server/notifications.d.ts +55 -0
- package/src/lib/server/notifications.js +134 -0
- package/src/lib/server/notifications.js.map +1 -0
- package/src/lib/server/org-billing.d.ts +97 -0
- package/src/lib/server/org-billing.js +236 -0
- package/src/lib/server/org-billing.js.map +1 -0
- package/src/lib/server/organizations.d.ts +837 -0
- package/src/lib/server/organizations.js +1814 -0
- package/src/lib/server/organizations.js.map +1 -0
- package/src/lib/server/password-reset-throttle.d.ts +84 -0
- package/src/lib/server/password-reset-throttle.js +102 -0
- package/src/lib/server/password-reset-throttle.js.map +1 -0
- package/src/lib/server/personal-data-export.d.ts +241 -0
- package/src/lib/server/personal-data-export.js +858 -0
- package/src/lib/server/personal-data-export.js.map +1 -0
- package/src/lib/server/platform-domain-names.d.ts +62 -0
- package/src/lib/server/platform-domain-names.js +197 -0
- package/src/lib/server/platform-domain-names.js.map +1 -0
- package/src/lib/server/platform-marketing-consent.d.ts +133 -0
- package/src/lib/server/platform-marketing-consent.js +209 -0
- package/src/lib/server/platform-marketing-consent.js.map +1 -0
- package/src/lib/server/rate-limit-store.d.ts +702 -0
- package/src/lib/server/rate-limit-store.js +1220 -0
- package/src/lib/server/rate-limit-store.js.map +1 -0
- package/src/lib/server/realm-plugins.d.ts +67 -0
- package/src/lib/server/realm-plugins.js +250 -0
- package/src/lib/server/realm-plugins.js.map +1 -0
- package/src/lib/server/recaptcha-allowlist.d.ts +90 -0
- package/src/lib/server/recaptcha-allowlist.js +409 -0
- package/src/lib/server/recaptcha-allowlist.js.map +1 -0
- package/src/lib/server/release-flags.d.ts +105 -0
- package/src/lib/server/release-flags.js +183 -0
- package/src/lib/server/release-flags.js.map +1 -0
- package/src/lib/server/replay-erasures.d.ts +126 -0
- package/src/lib/server/replay-erasures.js +217 -0
- package/src/lib/server/replay-erasures.js.map +1 -0
- package/src/lib/server/resolve-people.d.ts +66 -0
- package/src/lib/server/resolve-people.js +112 -0
- package/src/lib/server/resolve-people.js.map +1 -0
- package/src/lib/server/safe-equal.d.ts +45 -0
- package/src/lib/server/safe-equal.js +58 -0
- package/src/lib/server/safe-equal.js.map +1 -0
- package/src/lib/server/sending-domain-debt.d.ts +127 -0
- package/src/lib/server/sending-domain-debt.js +268 -0
- package/src/lib/server/sending-domain-debt.js.map +1 -0
- package/src/lib/server/sending-domain-recheck.d.ts +151 -0
- package/src/lib/server/sending-domain-recheck.js +253 -0
- package/src/lib/server/sending-domain-recheck.js.map +1 -0
- package/src/lib/server/sending-domains.d.ts +364 -0
- package/src/lib/server/sending-domains.js +683 -0
- package/src/lib/server/sending-domains.js.map +1 -0
- package/src/lib/server/serve-media-cdn.d.ts +433 -0
- package/src/lib/server/serve-media-cdn.js +1370 -0
- package/src/lib/server/serve-media-cdn.js.map +1 -0
- package/src/lib/server/serve-plugin-fetch.d.ts +61 -0
- package/src/lib/server/serve-plugin-fetch.js +294 -0
- package/src/lib/server/serve-plugin-fetch.js.map +1 -0
- package/src/lib/server/sms-keywords.d.ts +63 -0
- package/src/lib/server/sms-keywords.js +146 -0
- package/src/lib/server/sms-keywords.js.map +1 -0
- package/src/lib/server/sso-break-glass-owners.d.ts +91 -0
- package/src/lib/server/sso-break-glass-owners.js +227 -0
- package/src/lib/server/sso-break-glass-owners.js.map +1 -0
- package/src/lib/server/sso-domain-policy.d.ts +140 -0
- package/src/lib/server/sso-domain-policy.js +239 -0
- package/src/lib/server/sso-domain-policy.js.map +1 -0
- package/src/lib/server/sso-drift-logic.d.ts +106 -0
- package/src/lib/server/sso-drift-logic.js +95 -0
- package/src/lib/server/sso-drift-logic.js.map +1 -0
- package/src/lib/server/sso-enforcement.d.ts +204 -0
- package/src/lib/server/sso-enforcement.js +309 -0
- package/src/lib/server/sso-enforcement.js.map +1 -0
- package/src/lib/server/sso-provisioning.d.ts +320 -0
- package/src/lib/server/sso-provisioning.js +715 -0
- package/src/lib/server/sso-provisioning.js.map +1 -0
- package/src/lib/server/staff-alert-email.d.ts +41 -0
- package/src/lib/server/staff-alert-email.js +63 -0
- package/src/lib/server/staff-alert-email.js.map +1 -0
- package/src/lib/server/stripe-account-mode.d.ts +156 -0
- package/src/lib/server/stripe-account-mode.js +162 -0
- package/src/lib/server/stripe-account-mode.js.map +1 -0
- package/src/lib/server/tenant-write-lockdown.d.ts +131 -0
- package/src/lib/server/tenant-write-lockdown.js +176 -0
- package/src/lib/server/tenant-write-lockdown.js.map +1 -0
- package/src/lib/server/test-firestore.d.ts +45 -0
- package/src/lib/server/test-firestore.js +124 -0
- package/src/lib/server/test-firestore.js.map +1 -0
- package/src/lib/server/token-revocation.d.ts +140 -0
- package/src/lib/server/token-revocation.js +255 -0
- package/src/lib/server/token-revocation.js.map +1 -0
- package/src/lib/server/update-existing.d.ts +32 -0
- package/src/lib/server/update-existing.js +73 -0
- package/src/lib/server/update-existing.js.map +1 -0
- package/src/lib/server/upload-cors-reconcile.d.ts +118 -0
- package/src/lib/server/upload-cors-reconcile.js +234 -0
- package/src/lib/server/upload-cors-reconcile.js.map +1 -0
- package/src/lib/server/upsert-contact.d.ts +262 -0
- package/src/lib/server/upsert-contact.js +546 -0
- package/src/lib/server/upsert-contact.js.map +1 -0
- package/src/lib/server/usage-invoice.d.ts +203 -0
- package/src/lib/server/usage-invoice.js +397 -0
- package/src/lib/server/usage-invoice.js.map +1 -0
- package/src/lib/server/user-profiles.d.ts +151 -0
- package/src/lib/server/user-profiles.js +364 -0
- package/src/lib/server/user-profiles.js.map +1 -0
- package/src/lib/server/vercel-drain-signature.d.ts +30 -0
- package/src/lib/server/vercel-drain-signature.js +61 -0
- package/src/lib/server/vercel-drain-signature.js.map +1 -0
- package/src/lib/server/vercel-log-drain.d.ts +259 -0
- package/src/lib/server/vercel-log-drain.js +479 -0
- package/src/lib/server/vercel-log-drain.js.map +1 -0
- package/src/lib/server/verify-email-cooldown.d.ts +102 -0
- package/src/lib/server/verify-email-cooldown.js +114 -0
- package/src/lib/server/verify-email-cooldown.js.map +1 -0
- package/src/lib/server/visitor-write-rate-limit.d.ts +87 -0
- package/src/lib/server/visitor-write-rate-limit.js +117 -0
- package/src/lib/server/visitor-write-rate-limit.js.map +1 -0
- package/src/lib/server/workspace-domains.d.ts +156 -0
- package/src/lib/server/workspace-domains.js +271 -0
- package/src/lib/server/workspace-domains.js.map +1 -0
- package/src/tenancy.d.ts +115 -0
- package/src/tenancy.js +142 -0
- package/src/tenancy.js.map +1 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* MARKETING MAIL — the durable half.
|
|
19
|
+
*
|
|
20
|
+
* `marketing-send.ts` in `@aglyn/shared-util-email` states the policy and
|
|
21
|
+
* holds the injection seam; this answers the question, because answering it
|
|
22
|
+
* needs Firestore and that library may not hold the Admin SDK.
|
|
23
|
+
*
|
|
24
|
+
* Five things, in the order they cost:
|
|
25
|
+
*
|
|
26
|
+
* 1. **Both suppression lists**, through the one shared helper. A person who
|
|
27
|
+
* unsubscribed from this site, hard-bounced anywhere in the product, or
|
|
28
|
+
* pressed "report spam" is not mailed. Asked first because it is the only
|
|
29
|
+
* one whose answer is permanent.
|
|
30
|
+
* 2. **The pace the RECIPIENT asked for** on the preference page, if they
|
|
31
|
+
* asked for one.
|
|
32
|
+
* 3. **An engagement sunset**, when an operator has configured a window —
|
|
33
|
+
* off by default, and it costs a read only when it is on.
|
|
34
|
+
* 4. **A frequency ceiling**, per recipient per site, over a rolling day.
|
|
35
|
+
* 5. **The signed unsubscribe URL**, so the message carries a way out.
|
|
36
|
+
*
|
|
37
|
+
* ## The order of the three pace refusals, and who each one is for
|
|
38
|
+
*
|
|
39
|
+
* Suppression is first and needs no argument: its answer is permanent, and
|
|
40
|
+
* the frequency record must not count a message that was never going to
|
|
41
|
+
* leave. The other three are all refusals about PACE, and they are ordered by
|
|
42
|
+
* whose refusal it is, then by what asking costs.
|
|
43
|
+
*
|
|
44
|
+
* **The recipient's own cadence is second — above both platform controls.**
|
|
45
|
+
* It is the only one of the three a PERSON asked for; the sunset is an
|
|
46
|
+
* inference this platform drew about them and the ceiling is a guard against
|
|
47
|
+
* the merchant. When more than one applies, the honest answer to "why did
|
|
48
|
+
* this not send" is the fact somebody stated, not the guess we made. It is
|
|
49
|
+
* also the cheapest to ask: it is already on the counter document the ceiling
|
|
50
|
+
* reads, so a recipient who asked for monthly mail never pays for the
|
|
51
|
+
* sunset's engagement read. And it is the only one that binds a campaign, so
|
|
52
|
+
* putting it first makes the campaign path and the automated paths evaluate
|
|
53
|
+
* the same refusals in the same order rather than interleaving one control a
|
|
54
|
+
* campaign skips with one it does not.
|
|
55
|
+
*
|
|
56
|
+
* **The sunset is third, above the ceiling.** Its refusal is TERMINAL and the
|
|
57
|
+
* ceiling's is not: a sweep defers a `frequency-capped` message and retries
|
|
58
|
+
* it, so reporting the retryable refusal for a person the sunset would refuse
|
|
59
|
+
* anyway means the same doomed row comes back on every beat. That argument
|
|
60
|
+
* does not reach past the cadence, because a cadence gap is a day, a week or
|
|
61
|
+
* a month — a sweep retries such a row once per interval, not once per beat.
|
|
62
|
+
*
|
|
63
|
+
* ## Which refusals bind a campaign
|
|
64
|
+
*
|
|
65
|
+
* `MarketingSendContext.capped` is `false` for a campaign, and it governs
|
|
66
|
+
* exactly two of the four:
|
|
67
|
+
*
|
|
68
|
+
* | Refusal | A campaign is |
|
|
69
|
+
* | ------------------ | ------------- |
|
|
70
|
+
* | `suppressed` | **bound** |
|
|
71
|
+
* | `cadence-limited` | **bound** |
|
|
72
|
+
* | `unengaged` | exempt (`capped`) |
|
|
73
|
+
* | `frequency-capped` | exempt (`capped`) |
|
|
74
|
+
*
|
|
75
|
+
* The two exemptions share one reason: a campaign is a merchant's reviewed,
|
|
76
|
+
* one-shot act with a recipient count on screen before they press Send, and a
|
|
77
|
+
* platform control that silently removed people from that number would make
|
|
78
|
+
* it a lie. That reason is about a control the merchant did not ask for and
|
|
79
|
+
* cannot see. It does not reach a request the RECIPIENT made — a campaign
|
|
80
|
+
* that overrode the preference page would make it a form recording a choice
|
|
81
|
+
* nothing honors, which is ignoring an unsubscribe one notch quieter.
|
|
82
|
+
*
|
|
83
|
+
* A campaign COUNTS toward the ceiling either way; it is exempt from the
|
|
84
|
+
* refusal, never from the counting.
|
|
85
|
+
*
|
|
86
|
+
* ## The counter is a WINDOW, not a total
|
|
87
|
+
*
|
|
88
|
+
* `hosts/{hostId}/emailFrequency/{sha256(address)}` holds the instants of the
|
|
89
|
+
* marketing messages this site sent this person, trimmed to the window on
|
|
90
|
+
* every touch. A rolling window needs the instants; a running total would
|
|
91
|
+
* need a reset nobody triggers, and a per-day bucket would let a merchant
|
|
92
|
+
* send the whole ceiling at 23:55 and the whole ceiling again at 00:05.
|
|
93
|
+
*
|
|
94
|
+
* The record is bounded by the cap — a handful of numbers — because
|
|
95
|
+
* `marketingFrequencyVerdict` returns the trimmed window and this writes back
|
|
96
|
+
* only that. It is server-owned: the address is hashed exactly as the two
|
|
97
|
+
* suppression lists hash it, and no client writes or reads it.
|
|
98
|
+
*
|
|
99
|
+
* ## The check and the write are not one transaction, on purpose
|
|
100
|
+
*
|
|
101
|
+
* Two concurrent marketing sends to the same person can both read a window
|
|
102
|
+
* with room and both append, so the cap can be exceeded by the number of
|
|
103
|
+
* messages genuinely in flight at that instant. That is accepted rather than
|
|
104
|
+
* papered over: a transaction per recipient would double the round trips on a
|
|
105
|
+
* path that is already one awaited HTTP POST per person, and being off by one
|
|
106
|
+
* on a runaway guard costs a recipient one extra message. Being slow costs
|
|
107
|
+
* every recipient.
|
|
108
|
+
*
|
|
109
|
+
* ## Never a reduction of the person or their data
|
|
110
|
+
*
|
|
111
|
+
* A cap here refuses a SEND. It never removes a contact, never unsubscribes
|
|
112
|
+
* anybody, never trims an audience and never deletes a counter row belonging
|
|
113
|
+
* to somebody. `over-limit.ts` states the rule for capacity limits and this is
|
|
114
|
+
* the same instrument pointed at time: the message does not go, the person
|
|
115
|
+
* stays exactly where they were, and the next window mails them again. The
|
|
116
|
+
* recipient's own cadence is the same shape — a person who asked for monthly
|
|
117
|
+
* mail stays on every audience they were on and is mailed again next month.
|
|
118
|
+
*/
|
|
119
|
+
import { type MarketingCadence, type MarketingSendGateRequest, type MarketingSendGateVerdict } from '@aglyn/shared-util-email';
|
|
120
|
+
/**
|
|
121
|
+
* Where the per-recipient window lives.
|
|
122
|
+
*
|
|
123
|
+
* Under the host rather than the org: the ceiling is "how much mail this SITE
|
|
124
|
+
* sends one person", which is the unit a recipient experiences and the unit
|
|
125
|
+
* the unsubscribe link is scoped to. An org-wide counter would let one site's
|
|
126
|
+
* newsletter suppress a sibling site's order-related marketing, and the two
|
|
127
|
+
* sites may be unrelated brands.
|
|
128
|
+
*/
|
|
129
|
+
export declare const EMAIL_FREQUENCY_SUBCOLLECTION = "emailFrequency";
|
|
130
|
+
/** The stored window. */
|
|
131
|
+
export interface EmailFrequencyRecord {
|
|
132
|
+
/** The address, in the clear, lowercased. The id is its hash. */
|
|
133
|
+
email: string;
|
|
134
|
+
/** Marketing send instants, trimmed to the window on every touch. */
|
|
135
|
+
sentAtMs: number[];
|
|
136
|
+
/**
|
|
137
|
+
* When this site FIRST sent this person marketing mail.
|
|
138
|
+
*
|
|
139
|
+
* Written once and never again, and it rides here rather than in a store of
|
|
140
|
+
* its own because this document is already written on every marketing send:
|
|
141
|
+
* carrying the field costs nothing, and a second per-recipient document
|
|
142
|
+
* would be a write per send per person for one number.
|
|
143
|
+
*
|
|
144
|
+
* The sunset is what reads it — see `marketingSunsetVerdict`. A person we
|
|
145
|
+
* have not been mailing for longer than the sunset window cannot have been
|
|
146
|
+
* quiet for longer than the sunset window, so this is the field that keeps
|
|
147
|
+
* a new subscriber from being refused for having nothing on record yet.
|
|
148
|
+
*
|
|
149
|
+
* Null for every row written before this field existed, which reads as "no
|
|
150
|
+
* record" and therefore refuses nobody. That is the conservative direction
|
|
151
|
+
* for a field introduced after the data, and it self-heals: the row gains
|
|
152
|
+
* the stamp on this site's next marketing send to that person, and the
|
|
153
|
+
* clock starts from then rather than from a past nobody recorded.
|
|
154
|
+
*/
|
|
155
|
+
firstSentAtMs?: number;
|
|
156
|
+
/**
|
|
157
|
+
* The most recent marketing send, NEVER trimmed.
|
|
158
|
+
*
|
|
159
|
+
* `sentAtMs` is a rolling day, so after a quiet day it is empty and cannot
|
|
160
|
+
* answer "when did this site last mail this person" — which is the only
|
|
161
|
+
* question a weekly or monthly cadence asks. One number rather than a
|
|
162
|
+
* window kept for a month.
|
|
163
|
+
*
|
|
164
|
+
* The bookends of the same relationship: `firstSentAtMs` never moves and
|
|
165
|
+
* this one moves on every send, so between them they answer both "how long
|
|
166
|
+
* have we been mailing this person" and "how long since we last did".
|
|
167
|
+
*/
|
|
168
|
+
lastSentAtMs?: number;
|
|
169
|
+
/**
|
|
170
|
+
* How often the RECIPIENT asked to hear from this site.
|
|
171
|
+
*
|
|
172
|
+
* Stored on the counter document rather than beside the topic opt-outs, and
|
|
173
|
+
* that is the whole reason it costs nothing: the gate already reads this
|
|
174
|
+
* document on every marketing message, so honoring the preference adds no
|
|
175
|
+
* round trip to any send. It also puts the preference in the one document
|
|
176
|
+
* that is already about how much mail this site sends this person.
|
|
177
|
+
*
|
|
178
|
+
* Absent on every record written before it existed, which reads as no
|
|
179
|
+
* expressed preference — see `normalizeMarketingCadence`.
|
|
180
|
+
*/
|
|
181
|
+
cadence?: MarketingCadence;
|
|
182
|
+
/** When the recipient chose it, for the record that the request was made. */
|
|
183
|
+
cadenceSetAtMs?: number;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* What one recipient's counter document says, with its defaults applied.
|
|
187
|
+
*
|
|
188
|
+
* Everything the gate asks of Firestore about this person on this site, in
|
|
189
|
+
* one shape, because it is fetched in one round trip — see
|
|
190
|
+
* {@link readMarketingFrequencyState}.
|
|
191
|
+
*/
|
|
192
|
+
export interface MarketingFrequencyState {
|
|
193
|
+
/** Send instants inside the rolling window. */
|
|
194
|
+
window: number[];
|
|
195
|
+
/** When this site first mailed them, or `null` for no record. */
|
|
196
|
+
firstSentAtMs: number | null;
|
|
197
|
+
/** The most recent send, or `null` for somebody never mailed. */
|
|
198
|
+
lastSentAtMs: number | null;
|
|
199
|
+
/** The recipient's chosen pace. */
|
|
200
|
+
cadence: MarketingCadence;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* The instants inside the window for one recipient of one site's mail.
|
|
204
|
+
*
|
|
205
|
+
* FAILS OPEN — an empty window, meaning "nothing recorded, so there is room".
|
|
206
|
+
* The opposite of {@link filterSendableForHost}, and the difference is what
|
|
207
|
+
* each answer costs when it is wrong. A suppression read that fails open
|
|
208
|
+
* mails somebody who told us to stop; a frequency read that fails closed
|
|
209
|
+
* refuses a message nobody objected to, over a counter that is a runaway
|
|
210
|
+
* guard rather than a consent record.
|
|
211
|
+
*/
|
|
212
|
+
export declare function readMarketingFrequency(hostId: string, email: string, firestore?: any): Promise<number[]>;
|
|
213
|
+
/**
|
|
214
|
+
* The whole counter document — the window, the first send, the last send and
|
|
215
|
+
* the recipient's chosen pace — in ONE round trip.
|
|
216
|
+
*
|
|
217
|
+
* **One read, four facts, and that is a requirement rather than a tidiness.**
|
|
218
|
+
* Three separate refusals in {@link marketingSendVerdict} read this document,
|
|
219
|
+
* and a `get` per refusal would be three awaited round trips on a path that
|
|
220
|
+
* is already one awaited HTTP POST per recipient. Which is also why the
|
|
221
|
+
* recipient's cadence is stored HERE rather than beside the topic opt-outs:
|
|
222
|
+
* honoring a preference this gate has to consult on every marketing message
|
|
223
|
+
* has to cost nothing, and riding on a document already being read is the
|
|
224
|
+
* only shape that does. See {@link EmailFrequencyRecord.cadence}.
|
|
225
|
+
*
|
|
226
|
+
* FAILS OPEN on every field, and in the same direction each time: an empty
|
|
227
|
+
* window means "there is room", a null `firstSentAtMs` means "no record", a
|
|
228
|
+
* null `lastSentAtMs` means "never mailed", and an unreadable cadence reads
|
|
229
|
+
* as no preference expressed. An unreadable counter is not evidence that
|
|
230
|
+
* somebody asked for less, and it is not evidence that they have gone cold —
|
|
231
|
+
* the opposite of {@link filterSendableForHost}, for the reason
|
|
232
|
+
* {@link readMarketingFrequency} states.
|
|
233
|
+
*/
|
|
234
|
+
export declare function readMarketingFrequencyState(hostId: string, email: string, firestore?: any): Promise<MarketingFrequencyState>;
|
|
235
|
+
/**
|
|
236
|
+
* Records the pace a recipient asked for.
|
|
237
|
+
*
|
|
238
|
+
* Written from the preference page, which is unauthenticated and reached by a
|
|
239
|
+
* signed link — so the CALLER has already proved the request is this
|
|
240
|
+
* address's. A merge, because the document is the send counter and this must
|
|
241
|
+
* not disturb the window it shares with.
|
|
242
|
+
*
|
|
243
|
+
* Never throws: a preference that failed to store is a page that should say
|
|
244
|
+
* so, not a 500 on a screen a recipient reached in order to leave.
|
|
245
|
+
*
|
|
246
|
+
* @returns whether it was stored.
|
|
247
|
+
*/
|
|
248
|
+
export declare function setMarketingCadence(hostId: string, email: string, cadence: MarketingCadence, options?: {
|
|
249
|
+
nowMs?: number;
|
|
250
|
+
firestore?: any;
|
|
251
|
+
}): Promise<boolean>;
|
|
252
|
+
/**
|
|
253
|
+
* Records marketing messages against the recipients' windows.
|
|
254
|
+
*
|
|
255
|
+
* Takes a LIST because the campaign sender records a whole batch at once: it
|
|
256
|
+
* is exempt from the refusal (a reviewed, one-shot act whose recipient count
|
|
257
|
+
* is on screen before it goes) but not from the counting, since a campaign is
|
|
258
|
+
* most of the mail a person receives from a site. Doing that per recipient
|
|
259
|
+
* inside the send loop would add a round trip to each of up to 500 sequential
|
|
260
|
+
* sends; doing it here adds one batch after them.
|
|
261
|
+
*
|
|
262
|
+
* Never throws. A lost counter increment means one recipient's ceiling is
|
|
263
|
+
* measured a message low, which is not worth failing a delivered send over.
|
|
264
|
+
*/
|
|
265
|
+
export declare function recordMarketingSends(hostId: string, emails: readonly string[], options?: {
|
|
266
|
+
nowMs?: number;
|
|
267
|
+
firestore?: any;
|
|
268
|
+
}): Promise<number>;
|
|
269
|
+
/**
|
|
270
|
+
* The subset of `emails` that has NOT asked this site for mail less often
|
|
271
|
+
* than right now.
|
|
272
|
+
*
|
|
273
|
+
* The fourth filter a campaign passes, after the platform suppression list,
|
|
274
|
+
* the site's own and the topic opt-outs — and the only one that is not in
|
|
275
|
+
* `email-suppression.ts` beside those three, because it reads the counter
|
|
276
|
+
* document this module owns and that module is this module's dependency.
|
|
277
|
+
*
|
|
278
|
+
* {@link marketingSendVerdict} enforces the same rule per message, for the
|
|
279
|
+
* senders that reach the gate. THIS is the campaign path's copy, and it is
|
|
280
|
+
* the enforcement there rather than only a count: a campaign carries no
|
|
281
|
+
* `marketing` context — it mints its own unsubscribe URL upstream — so the
|
|
282
|
+
* gate is not on its path at all, and a rule asked only there would not be
|
|
283
|
+
* asked of the sender that produces most of a person's mail.
|
|
284
|
+
*
|
|
285
|
+
* Answering it as a FILTER rather than per message is also the only placement
|
|
286
|
+
* that keeps the composer honest. The argument for exempting a campaign from
|
|
287
|
+
* the platform ceiling is that a control which silently removed people from a
|
|
288
|
+
* reviewed one-shot send would make the number on screen a lie; subtracting
|
|
289
|
+
* a request the recipient actually made, where every other refusal is already
|
|
290
|
+
* subtracted, is what keeps this one from having that problem.
|
|
291
|
+
*
|
|
292
|
+
* Keyed and read with one `getAll`, matching its three neighbors: one round
|
|
293
|
+
* trip bounded by the size of the send, and no composite index to go missing.
|
|
294
|
+
*
|
|
295
|
+
* ## Fails OPEN, like the topic filter beside it
|
|
296
|
+
*
|
|
297
|
+
* A cadence is a PACE, not a stop. Guessing wrong on an unreadable counter
|
|
298
|
+
* costs one recipient one message sooner than they asked for; guessing wrong
|
|
299
|
+
* the other way withholds a whole campaign on a transient read failure. The
|
|
300
|
+
* two suppression lists have already refused everybody who asked us to stop
|
|
301
|
+
* entirely, so nobody who said "no" reaches this line.
|
|
302
|
+
*/
|
|
303
|
+
export declare function filterCadenceSendable(hostId: string, emails: readonly string[], options?: {
|
|
304
|
+
nowMs?: number;
|
|
305
|
+
firestore?: any;
|
|
306
|
+
}): Promise<string[]>;
|
|
307
|
+
/**
|
|
308
|
+
* The gate itself, exported so it can be exercised without installing it.
|
|
309
|
+
*
|
|
310
|
+
* ORDER MATTERS. The suppression check is first because its answer is
|
|
311
|
+
* permanent and the frequency record must not count a message that was never
|
|
312
|
+
* going to leave — a suppressed address whose window kept growing would stay
|
|
313
|
+
* capped for a day after being released.
|
|
314
|
+
*/
|
|
315
|
+
export declare function marketingSendVerdict(request: MarketingSendGateRequest, options?: {
|
|
316
|
+
nowMs?: number;
|
|
317
|
+
firestore?: any;
|
|
318
|
+
}): Promise<MarketingSendGateVerdict>;
|
|
319
|
+
/**
|
|
320
|
+
* Puts the gate on `sendEmail`'s path.
|
|
321
|
+
*
|
|
322
|
+
* **Called at module load**, from the bottom of this file, so that importing
|
|
323
|
+
* `@aglyn/tenant-data-admin` anywhere is enough — every server surface in the
|
|
324
|
+
* product already imports that barrel, and `export *` forces this module to
|
|
325
|
+
* evaluate. The alternative, a call at each server entrypoint, is the
|
|
326
|
+
* many-places-to-remember shape this codebase has rejected twice already.
|
|
327
|
+
*
|
|
328
|
+
* Idempotent: installing twice replaces the same closure with an equivalent
|
|
329
|
+
* one, and the closure holds no state — the state is the Firestore document.
|
|
330
|
+
*/
|
|
331
|
+
export declare function installMarketingSendGate(): void;
|
|
332
|
+
/** Whether a gate is installed. Reads the shared seam, not a local flag. */
|
|
333
|
+
export declare function isMarketingSendGateInstalled(): boolean;
|