@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,272 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { FieldValue } from "firebase-admin/firestore";
|
|
18
|
+
// The leaves, not the barrel — see `email-suppression.ts` for why: a spec
|
|
19
|
+
// of this sweep substitutes a store, not the pure helpers it leans on.
|
|
20
|
+
import { normalizeContactEmail } from "@aglyn/aglyn/app-utils/contacts";
|
|
21
|
+
import { CRM_COLLECTIONS } from "@aglyn/aglyn/app-utils/crm";
|
|
22
|
+
import { personKey } from "@aglyn/aglyn/app-utils/person-key";
|
|
23
|
+
import { runPluginPersonErasers } from "@aglyn/aglyn/plugin-manager/plugin-person-erasure";
|
|
24
|
+
import { companyContactsCountFields } from "./contact-company-link.js";
|
|
25
|
+
import { eraseEmailDeliveriesForAddresses } from "./email-delivery-log.js";
|
|
26
|
+
import { suppressEmailForHostErasure } from "./email-suppression.js";
|
|
27
|
+
import firebaseAdmin from "./firebase-admin.js";
|
|
28
|
+
import { listMemberDocIds } from "./list-members.js";
|
|
29
|
+
const defaultFirestore = ()=>firebaseAdmin.app().firestore();
|
|
30
|
+
/** Firestore caps a batch at 500 writes; a page under it keeps a margin. */ const PAGE = 400;
|
|
31
|
+
/**
|
|
32
|
+
* A pathological collection must not hold the erasure open indefinitely:
|
|
33
|
+
* twenty pages of four hundred is eight thousand rows of one kind for one
|
|
34
|
+
* person, past anything a real relationship produces.
|
|
35
|
+
*/ const MAX_PAGES = 20;
|
|
36
|
+
/**
|
|
37
|
+
* Remove one person from one workspace (AGL-2623).
|
|
38
|
+
*
|
|
39
|
+
* `planContactDetach` is deliberately not consulted: the CRM's delete is a
|
|
40
|
+
* detach that leaves the row for the other holders, and this is the act
|
|
41
|
+
* that must not. The contact document goes whole — every site's facet,
|
|
42
|
+
* every consent entry, every attribution — and with it everything the
|
|
43
|
+
* workspace keeps beside the person.
|
|
44
|
+
*
|
|
45
|
+
* ## Order
|
|
46
|
+
*
|
|
47
|
+
* 1. **Suppress first.** One row per site on the per-site suppression
|
|
48
|
+
* list, before any delete: a form filled in while the sweep runs must
|
|
49
|
+
* already find the door closed, or the sweep deletes a row that the
|
|
50
|
+
* capture re-creates a moment later.
|
|
51
|
+
* 2. The plugins' share: every eraser registered on
|
|
52
|
+
* `plugin-person-erasure`, handed the address, its key and the ids of
|
|
53
|
+
* the contacts about to go, while those documents still exist.
|
|
54
|
+
* 3. The contact and its satellites — company counts, deals unlinked,
|
|
55
|
+
* tasks and activities deleted — by the contact's id, then the
|
|
56
|
+
* document itself.
|
|
57
|
+
* 4. Leads, list memberships, orders and bookings by the address, on
|
|
58
|
+
* every site of the workspace.
|
|
59
|
+
* 5. The delivery log, last: it is filed under the address alone, and the
|
|
60
|
+
* tombstone it leaves is what keeps a later import from refilling it.
|
|
61
|
+
*
|
|
62
|
+
* ## What is anonymized rather than deleted
|
|
63
|
+
*
|
|
64
|
+
* An order is the merchant's record of a sale and a booking of an
|
|
65
|
+
* appointment; the amounts, the line items and the tax are theirs to keep
|
|
66
|
+
* and the law expects them kept. The person is taken OFF those records —
|
|
67
|
+
* name, email, phone, addresses — and a stamp says when. A deal is the
|
|
68
|
+
* team's own pipeline record and is unlinked. Everything else that names
|
|
69
|
+
* the person is about the person and is deleted.
|
|
70
|
+
*
|
|
71
|
+
* ## What is not reached, and why
|
|
72
|
+
*
|
|
73
|
+
* A form submission keeps the address inside `fields`, under whatever the
|
|
74
|
+
* form called it — there is no key to query by and a scan of every
|
|
75
|
+
* submission on every site is unbounded. A site member's login is their
|
|
76
|
+
* own account. A subscription carries the address it bills, and a live one
|
|
77
|
+
* cannot be anonymized without breaking its receipts. Each is named to the
|
|
78
|
+
* admin by the dialog so they can finish by hand.
|
|
79
|
+
*
|
|
80
|
+
* Every sweep is best-effort against the others: a failure in one is
|
|
81
|
+
* logged and counted as zero, and the request's counts say what happened.
|
|
82
|
+
* The caller decides whether zero contacts on a person it could see is a
|
|
83
|
+
* failure to retry.
|
|
84
|
+
*/ export async function erasePerson(options) {
|
|
85
|
+
var _options_firestore, _options_now;
|
|
86
|
+
const email = normalizeContactEmail(options.email);
|
|
87
|
+
const key = email ? personKey(email) : null;
|
|
88
|
+
if (!email || !key) return {
|
|
89
|
+
ok: false,
|
|
90
|
+
skippedReason: 'invalid-email'
|
|
91
|
+
};
|
|
92
|
+
const db = (_options_firestore = options.firestore) != null ? _options_firestore : defaultFirestore();
|
|
93
|
+
const now = (_options_now = options.now) != null ? _options_now : Date.now();
|
|
94
|
+
const orgRef = db.collection('orgs').doc(options.orgId);
|
|
95
|
+
const counts = {
|
|
96
|
+
hosts: 0,
|
|
97
|
+
hostsSuppressed: 0,
|
|
98
|
+
contacts: 0,
|
|
99
|
+
companyLinks: 0,
|
|
100
|
+
deals: 0,
|
|
101
|
+
tasks: 0,
|
|
102
|
+
activities: 0,
|
|
103
|
+
leads: 0,
|
|
104
|
+
listMemberships: 0,
|
|
105
|
+
orders: 0,
|
|
106
|
+
bookings: 0,
|
|
107
|
+
emailDeliveries: 0,
|
|
108
|
+
plugins: {}
|
|
109
|
+
};
|
|
110
|
+
const hosts = await db.collection('hosts').where('orgId', '==', options.orgId).get();
|
|
111
|
+
const hostIds = hosts.docs.map((doc)=>String(doc.id));
|
|
112
|
+
counts.hosts = hostIds.length;
|
|
113
|
+
for (const hostId of hostIds){
|
|
114
|
+
try {
|
|
115
|
+
const written = await suppressEmailForHostErasure({
|
|
116
|
+
hostId,
|
|
117
|
+
email,
|
|
118
|
+
firestore: db
|
|
119
|
+
});
|
|
120
|
+
if (written) counts.hostsSuppressed += 1;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(`erasePerson: suppression write failed for ${hostId}`, error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const contacts = await orgRef.collection('contacts').where('email', '==', email).get();
|
|
126
|
+
// What the plugins keep about the person (AGL-2981): records filed under
|
|
127
|
+
// the organization by the contact or by the address, which none of the
|
|
128
|
+
// sweeps here can see. Every site's door is already closed above, and
|
|
129
|
+
// the contacts are still there for an eraser that reads one. Isolated
|
|
130
|
+
// per plugin: one that fails is recorded as `null` and the erasure goes
|
|
131
|
+
// on. This erasure has no plan of its own, so it is never a dry run.
|
|
132
|
+
counts.plugins = await runPluginPersonErasers({
|
|
133
|
+
orgId: options.orgId,
|
|
134
|
+
email,
|
|
135
|
+
key,
|
|
136
|
+
contactIds: contacts.docs.map((contact)=>String(contact.id)),
|
|
137
|
+
dryRun: false
|
|
138
|
+
});
|
|
139
|
+
for (const contact of contacts.docs){
|
|
140
|
+
const contactId = String(contact.id);
|
|
141
|
+
const companyIds = contact.get('companyIds');
|
|
142
|
+
const linked = Array.isArray(companyIds) ? companyIds.filter((id)=>typeof id === 'string' && !!id) : [];
|
|
143
|
+
for (const companyId of new Set(linked)){
|
|
144
|
+
try {
|
|
145
|
+
await orgRef.collection(CRM_COLLECTIONS.companies).doc(companyId).update(companyContactsCountFields(-1));
|
|
146
|
+
counts.companyLinks += 1;
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error(`erasePerson: company count could not move for ${companyId}`, error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
counts.deals += await updateWhere(db, orgRef.collection(CRM_COLLECTIONS.deals), 'contactId', contactId, {
|
|
152
|
+
contactId: FieldValue.delete(),
|
|
153
|
+
updatedAt: FieldValue.serverTimestamp()
|
|
154
|
+
});
|
|
155
|
+
counts.tasks += await deleteWhere(db, orgRef.collection(CRM_COLLECTIONS.tasks), 'contactId', contactId);
|
|
156
|
+
counts.activities += await deleteWhere(db, orgRef.collection(CRM_COLLECTIONS.activities), 'contactId', contactId);
|
|
157
|
+
try {
|
|
158
|
+
await contact.ref.delete();
|
|
159
|
+
counts.contacts += 1;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error(`erasePerson: contact delete failed for ${contactId}`, error);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const erasedOrder = {
|
|
165
|
+
customerEmail: null,
|
|
166
|
+
customerName: null,
|
|
167
|
+
shippingAddress: FieldValue.delete(),
|
|
168
|
+
billingAddress: FieldValue.delete(),
|
|
169
|
+
customerErasedAtMs: now
|
|
170
|
+
};
|
|
171
|
+
const erasedBooking = {
|
|
172
|
+
email: null,
|
|
173
|
+
name: FieldValue.delete(),
|
|
174
|
+
phone: FieldValue.delete(),
|
|
175
|
+
customerErasedAtMs: now
|
|
176
|
+
};
|
|
177
|
+
for (const hostId of hostIds){
|
|
178
|
+
const hostRef = db.collection('hosts').doc(hostId);
|
|
179
|
+
try {
|
|
180
|
+
const lead = hostRef.collection('leads').doc(key);
|
|
181
|
+
if ((await lead.get()).exists) {
|
|
182
|
+
await lead.delete();
|
|
183
|
+
counts.leads += 1;
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error(`erasePerson: lead delete failed for ${hostId}`, error);
|
|
187
|
+
}
|
|
188
|
+
counts.orders += await updateWhere(db, hostRef.collection('orders'), 'customerEmail', email, erasedOrder);
|
|
189
|
+
counts.bookings += await updateWhere(db, hostRef.collection('bookings'), 'email', email, erasedBooking);
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
const memberIds = listMemberDocIds(email);
|
|
193
|
+
const lists = await orgRef.collection('lists').get();
|
|
194
|
+
for (const list of lists.docs){
|
|
195
|
+
const refs = memberIds.map((id)=>list.ref.collection('members').doc(id));
|
|
196
|
+
const found = await db.getAll(...refs);
|
|
197
|
+
for (const member of found){
|
|
198
|
+
if (!member.exists) continue;
|
|
199
|
+
await member.ref.delete();
|
|
200
|
+
counts.listMemberships += 1;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error('erasePerson: list membership sweep failed', error);
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const sweep = await eraseEmailDeliveriesForAddresses([
|
|
208
|
+
{
|
|
209
|
+
address: email
|
|
210
|
+
}
|
|
211
|
+
], db);
|
|
212
|
+
counts.emailDeliveries = sweep.removed;
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error('erasePerson: delivery log sweep failed', error);
|
|
215
|
+
}
|
|
216
|
+
await db.collection('adminAudit').add({
|
|
217
|
+
actorUid: 'system:erase-person',
|
|
218
|
+
action: 'person.erased',
|
|
219
|
+
target: `orgs/${options.orgId}/people/${key}`,
|
|
220
|
+
before: null,
|
|
221
|
+
after: counts,
|
|
222
|
+
at: FieldValue.serverTimestamp()
|
|
223
|
+
}).catch(()=>undefined);
|
|
224
|
+
return _extends({
|
|
225
|
+
ok: true
|
|
226
|
+
}, counts);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Delete every document in `collection` whose `field` equals `value`, a
|
|
230
|
+
* page at a time. Returns how many went; a failure logs and stops, and the
|
|
231
|
+
* count says how far it got.
|
|
232
|
+
*/ async function deleteWhere(db, collection, field, value) {
|
|
233
|
+
let removed = 0;
|
|
234
|
+
try {
|
|
235
|
+
for(let pass = 0; pass < MAX_PAGES; pass += 1){
|
|
236
|
+
const page = await collection.where(field, '==', value).limit(PAGE).get();
|
|
237
|
+
if (page.empty) break;
|
|
238
|
+
const batch = db.batch();
|
|
239
|
+
page.docs.forEach((doc)=>batch.delete(doc.ref));
|
|
240
|
+
await batch.commit();
|
|
241
|
+
removed += page.size;
|
|
242
|
+
if (page.size < PAGE) break;
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(`erasePerson: delete sweep failed on ${field}`, error);
|
|
246
|
+
}
|
|
247
|
+
return removed;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Apply `patch` to every document in `collection` whose `field` equals
|
|
251
|
+
* `value`. Because the patch clears the very field that was matched on, a
|
|
252
|
+
* second page never sees the first page's rows again, so the loop needs no
|
|
253
|
+
* cursor; the page cap is the only bound.
|
|
254
|
+
*/ async function updateWhere(db, collection, field, value, patch) {
|
|
255
|
+
let changed = 0;
|
|
256
|
+
try {
|
|
257
|
+
for(let pass = 0; pass < MAX_PAGES; pass += 1){
|
|
258
|
+
const page = await collection.where(field, '==', value).limit(PAGE).get();
|
|
259
|
+
if (page.empty) break;
|
|
260
|
+
const batch = db.batch();
|
|
261
|
+
page.docs.forEach((doc)=>batch.update(doc.ref, patch));
|
|
262
|
+
await batch.commit();
|
|
263
|
+
changed += page.size;
|
|
264
|
+
if (page.size < PAGE) break;
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error(`erasePerson: update sweep failed on ${field}`, error);
|
|
268
|
+
}
|
|
269
|
+
return changed;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
//# sourceMappingURL=erase-person.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/erase-person.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 { FieldValue } from 'firebase-admin/firestore'\n// The leaves, not the barrel — see `email-suppression.ts` for why: a spec\n// of this sweep substitutes a store, not the pure helpers it leans on.\nimport { normalizeContactEmail } from '@aglyn/aglyn/app-utils/contacts'\nimport { CRM_COLLECTIONS } from '@aglyn/aglyn/app-utils/crm'\nimport { personKey } from '@aglyn/aglyn/app-utils/person-key'\nimport {\n runPluginPersonErasers,\n type PluginPersonErasureReport,\n} from '@aglyn/aglyn/plugin-manager/plugin-person-erasure'\nimport { companyContactsCountFields } from './contact-company-link'\nimport { eraseEmailDeliveriesForAddresses } from './email-delivery-log'\nimport { suppressEmailForHostErasure } from './email-suppression'\nimport firebaseAdmin from './firebase-admin'\nimport { listMemberDocIds } from './list-members'\n\nconst defaultFirestore = () => firebaseAdmin.app().firestore()\n\n/** Firestore caps a batch at 500 writes; a page under it keeps a margin. */\nconst PAGE = 400\n\n/**\n * A pathological collection must not hold the erasure open indefinitely:\n * twenty pages of four hundred is eight thousand rows of one kind for one\n * person, past anything a real relationship produces.\n */\nconst MAX_PAGES = 20\n\nexport interface ErasePersonOptions {\n orgId: string\n /** Any spelling; normalized before anything is looked up. */\n email: unknown\n /** Injectable for tests; defaults to the admin app's Firestore. */\n firestore?: any\n /** Injectable for tests; defaults to `Date.now()`. */\n now?: number\n}\n\n/**\n * Counts, never identities: this is what the request document and the\n * audit row record, and both are read by people who must not learn the\n * address from them.\n */\nexport interface ErasePersonCounts {\n /** Sites in the workspace the sweep walked. */\n hosts: number\n /** Suppression rows written, one per site. */\n hostsSuppressed: number\n /** Contact documents deleted — the row every site shared. */\n contacts: number\n /** Companies whose contact count moved down. */\n companyLinks: number\n /** Deals whose `contactId` was removed. */\n deals: number\n tasks: number\n activities: number\n /** `hosts/{hostId}/leads/{personKey}` rows deleted. */\n leads: number\n /** Audience-list member rows deleted, across every list. */\n listMemberships: number\n /** Orders with the buyer's identity removed; the record stays. */\n orders: number\n /** Bookings with the person's identity removed; the record stays. */\n bookings: number\n /** Delivery-log messages deleted under the address. */\n emailDeliveries: number\n /**\n * Each plugin's share (AGL-2981), by plugin id: its eraser's own counts,\n * or `null` for an eraser that failed — its data may remain.\n */\n plugins: Record<string, PluginPersonErasureReport | null>\n}\n\nexport type ErasePersonResult =\n | ({ ok: true } & ErasePersonCounts)\n | { ok: false; skippedReason: 'invalid-email' }\n\n/**\n * Remove one person from one workspace (AGL-2623).\n *\n * `planContactDetach` is deliberately not consulted: the CRM's delete is a\n * detach that leaves the row for the other holders, and this is the act\n * that must not. The contact document goes whole — every site's facet,\n * every consent entry, every attribution — and with it everything the\n * workspace keeps beside the person.\n *\n * ## Order\n *\n * 1. **Suppress first.** One row per site on the per-site suppression\n * list, before any delete: a form filled in while the sweep runs must\n * already find the door closed, or the sweep deletes a row that the\n * capture re-creates a moment later.\n * 2. The plugins' share: every eraser registered on\n * `plugin-person-erasure`, handed the address, its key and the ids of\n * the contacts about to go, while those documents still exist.\n * 3. The contact and its satellites — company counts, deals unlinked,\n * tasks and activities deleted — by the contact's id, then the\n * document itself.\n * 4. Leads, list memberships, orders and bookings by the address, on\n * every site of the workspace.\n * 5. The delivery log, last: it is filed under the address alone, and the\n * tombstone it leaves is what keeps a later import from refilling it.\n *\n * ## What is anonymized rather than deleted\n *\n * An order is the merchant's record of a sale and a booking of an\n * appointment; the amounts, the line items and the tax are theirs to keep\n * and the law expects them kept. The person is taken OFF those records —\n * name, email, phone, addresses — and a stamp says when. A deal is the\n * team's own pipeline record and is unlinked. Everything else that names\n * the person is about the person and is deleted.\n *\n * ## What is not reached, and why\n *\n * A form submission keeps the address inside `fields`, under whatever the\n * form called it — there is no key to query by and a scan of every\n * submission on every site is unbounded. A site member's login is their\n * own account. A subscription carries the address it bills, and a live one\n * cannot be anonymized without breaking its receipts. Each is named to the\n * admin by the dialog so they can finish by hand.\n *\n * Every sweep is best-effort against the others: a failure in one is\n * logged and counted as zero, and the request's counts say what happened.\n * The caller decides whether zero contacts on a person it could see is a\n * failure to retry.\n */\nexport async function erasePerson(\n options: ErasePersonOptions,\n): Promise<ErasePersonResult> {\n const email = normalizeContactEmail(options.email)\n const key = email ? personKey(email) : null\n if (!email || !key) return { ok: false, skippedReason: 'invalid-email' }\n const db = options.firestore ?? defaultFirestore()\n const now = options.now ?? Date.now()\n const orgRef = db.collection('orgs').doc(options.orgId)\n\n const counts: ErasePersonCounts = {\n hosts: 0,\n hostsSuppressed: 0,\n contacts: 0,\n companyLinks: 0,\n deals: 0,\n tasks: 0,\n activities: 0,\n leads: 0,\n listMemberships: 0,\n orders: 0,\n bookings: 0,\n emailDeliveries: 0,\n plugins: {},\n }\n\n const hosts = await db\n .collection('hosts')\n .where('orgId', '==', options.orgId)\n .get()\n const hostIds: string[] = hosts.docs.map((doc: any) => String(doc.id))\n counts.hosts = hostIds.length\n\n for (const hostId of hostIds) {\n try {\n const written = await suppressEmailForHostErasure({ hostId, email, firestore: db })\n if (written) counts.hostsSuppressed += 1\n } catch (error) {\n console.error(`erasePerson: suppression write failed for ${hostId}`, error)\n }\n }\n\n const contacts = await orgRef.collection('contacts').where('email', '==', email).get()\n\n // What the plugins keep about the person (AGL-2981): records filed under\n // the organization by the contact or by the address, which none of the\n // sweeps here can see. Every site's door is already closed above, and\n // the contacts are still there for an eraser that reads one. Isolated\n // per plugin: one that fails is recorded as `null` and the erasure goes\n // on. This erasure has no plan of its own, so it is never a dry run.\n counts.plugins = await runPluginPersonErasers({\n orgId: options.orgId,\n email,\n key,\n contactIds: contacts.docs.map((contact: any) => String(contact.id)),\n dryRun: false,\n })\n\n for (const contact of contacts.docs) {\n const contactId = String(contact.id)\n const companyIds: unknown = contact.get('companyIds')\n const linked = Array.isArray(companyIds)\n ? companyIds.filter((id): id is string => typeof id === 'string' && !!id)\n : []\n for (const companyId of new Set(linked)) {\n try {\n await orgRef\n .collection(CRM_COLLECTIONS.companies)\n .doc(companyId)\n .update(companyContactsCountFields(-1))\n counts.companyLinks += 1\n } catch (error) {\n console.error(`erasePerson: company count could not move for ${companyId}`, error)\n }\n }\n counts.deals += await updateWhere(\n db,\n orgRef.collection(CRM_COLLECTIONS.deals),\n 'contactId',\n contactId,\n { contactId: FieldValue.delete(), updatedAt: FieldValue.serverTimestamp() },\n )\n counts.tasks += await deleteWhere(db, orgRef.collection(CRM_COLLECTIONS.tasks), 'contactId', contactId)\n counts.activities += await deleteWhere(\n db,\n orgRef.collection(CRM_COLLECTIONS.activities),\n 'contactId',\n contactId,\n )\n try {\n await contact.ref.delete()\n counts.contacts += 1\n } catch (error) {\n console.error(`erasePerson: contact delete failed for ${contactId}`, error)\n }\n }\n\n const erasedOrder = {\n customerEmail: null,\n customerName: null,\n shippingAddress: FieldValue.delete(),\n billingAddress: FieldValue.delete(),\n customerErasedAtMs: now,\n }\n const erasedBooking = {\n email: null,\n name: FieldValue.delete(),\n phone: FieldValue.delete(),\n customerErasedAtMs: now,\n }\n for (const hostId of hostIds) {\n const hostRef = db.collection('hosts').doc(hostId)\n try {\n const lead = hostRef.collection('leads').doc(key)\n if ((await lead.get()).exists) {\n await lead.delete()\n counts.leads += 1\n }\n } catch (error) {\n console.error(`erasePerson: lead delete failed for ${hostId}`, error)\n }\n counts.orders += await updateWhere(db, hostRef.collection('orders'), 'customerEmail', email, erasedOrder)\n counts.bookings += await updateWhere(db, hostRef.collection('bookings'), 'email', email, erasedBooking)\n }\n\n try {\n const memberIds = listMemberDocIds(email)\n const lists = await orgRef.collection('lists').get()\n for (const list of lists.docs) {\n const refs = memberIds.map((id) => list.ref.collection('members').doc(id))\n const found = await db.getAll(...refs)\n for (const member of found) {\n if (!member.exists) continue\n await member.ref.delete()\n counts.listMemberships += 1\n }\n }\n } catch (error) {\n console.error('erasePerson: list membership sweep failed', error)\n }\n\n try {\n const sweep = await eraseEmailDeliveriesForAddresses([{ address: email }], db)\n counts.emailDeliveries = sweep.removed\n } catch (error) {\n console.error('erasePerson: delivery log sweep failed', error)\n }\n\n await db\n .collection('adminAudit')\n .add({\n actorUid: 'system:erase-person',\n action: 'person.erased',\n target: `orgs/${options.orgId}/people/${key}`,\n before: null,\n after: counts,\n at: FieldValue.serverTimestamp(),\n })\n .catch(() => undefined)\n\n return { ok: true, ...counts }\n}\n\n/**\n * Delete every document in `collection` whose `field` equals `value`, a\n * page at a time. Returns how many went; a failure logs and stops, and the\n * count says how far it got.\n */\nasync function deleteWhere(\n db: any,\n collection: any,\n field: string,\n value: string,\n): Promise<number> {\n let removed = 0\n try {\n for (let pass = 0; pass < MAX_PAGES; pass += 1) {\n const page = await collection.where(field, '==', value).limit(PAGE).get()\n if (page.empty) break\n const batch = db.batch()\n page.docs.forEach((doc: any) => batch.delete(doc.ref))\n await batch.commit()\n removed += page.size\n if (page.size < PAGE) break\n }\n } catch (error) {\n console.error(`erasePerson: delete sweep failed on ${field}`, error)\n }\n return removed\n}\n\n/**\n * Apply `patch` to every document in `collection` whose `field` equals\n * `value`. Because the patch clears the very field that was matched on, a\n * second page never sees the first page's rows again, so the loop needs no\n * cursor; the page cap is the only bound.\n */\nasync function updateWhere(\n db: any,\n collection: any,\n field: string,\n value: string,\n patch: Record<string, unknown>,\n): Promise<number> {\n let changed = 0\n try {\n for (let pass = 0; pass < MAX_PAGES; pass += 1) {\n const page = await collection.where(field, '==', value).limit(PAGE).get()\n if (page.empty) break\n const batch = db.batch()\n page.docs.forEach((doc: any) => batch.update(doc.ref, patch))\n await batch.commit()\n changed += page.size\n if (page.size < PAGE) break\n }\n } catch (error) {\n console.error(`erasePerson: update sweep failed on ${field}`, error)\n }\n return changed\n}\n"],"names":["FieldValue","normalizeContactEmail","CRM_COLLECTIONS","personKey","runPluginPersonErasers","companyContactsCountFields","eraseEmailDeliveriesForAddresses","suppressEmailForHostErasure","firebaseAdmin","listMemberDocIds","defaultFirestore","app","firestore","PAGE","MAX_PAGES","erasePerson","options","email","key","ok","skippedReason","db","now","Date","orgRef","collection","doc","orgId","counts","hosts","hostsSuppressed","contacts","companyLinks","deals","tasks","activities","leads","listMemberships","orders","bookings","emailDeliveries","plugins","where","get","hostIds","docs","map","String","id","length","hostId","written","error","console","contactIds","contact","dryRun","contactId","companyIds","linked","Array","isArray","filter","companyId","Set","companies","update","updateWhere","delete","updatedAt","serverTimestamp","deleteWhere","ref","erasedOrder","customerEmail","customerName","shippingAddress","billingAddress","customerErasedAtMs","erasedBooking","name","phone","hostRef","lead","exists","memberIds","lists","list","refs","found","getAll","member","sweep","address","removed","add","actorUid","action","target","before","after","at","catch","undefined","field","value","pass","page","limit","empty","batch","forEach","commit","size","patch","changed"],"mappings":"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"}
|