@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,198 @@
|
|
|
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 { unstable_cache } from "next/cache";
|
|
17
|
+
/**
|
|
18
|
+
* Cross-request caching for the tenant render path's Firestore reads
|
|
19
|
+
* (AGL-1302, the caching half of AGL-1151).
|
|
20
|
+
*
|
|
21
|
+
* Every tenant SSR render fanned out to ~15–40 uncached Admin-SDK reads —
|
|
22
|
+
* host resolution, org billing, screen + version, the layout chain, and the
|
|
23
|
+
* whole compose bundle (components, variables, functions, datasets,
|
|
24
|
+
* workflows, plugin installs). None of that data is per-visitor, and almost
|
|
25
|
+
* all of it is identical across every path on a site, so a 50-route sweep
|
|
26
|
+
* paid the same host-scoped reads 50 times over. Routine verification
|
|
27
|
+
* traffic alone burned ~44K reads/day (87% of the free tier).
|
|
28
|
+
*
|
|
29
|
+
* This module is deliberately a THIN wrapper around `unstable_cache` rather
|
|
30
|
+
* than a cache of its own, so invalidation stays Next's: entries are tagged
|
|
31
|
+
* and the existing publish/revalidate path (`/api/revalidate`, AGL-1150)
|
|
32
|
+
* busts them with `revalidateTag` the same moment it drops the page HTML.
|
|
33
|
+
*
|
|
34
|
+
* Ground rules encoded here, because `unstable_cache` enforces none of them:
|
|
35
|
+
*
|
|
36
|
+
* - VALUES ARE JSON. `unstable_cache` stores `JSON.stringify(result)`, so a
|
|
37
|
+
* cache HIT returns plain data while a MISS would return live objects —
|
|
38
|
+
* Firestore `Timestamp`s, most importantly, whose `.seconds` getter decays
|
|
39
|
+
* to `_seconds` across the round trip. Serving two shapes for the same
|
|
40
|
+
* read is how a schedule check works in testing and misfires in
|
|
41
|
+
* production, so `withRenderCache` normalizes the MISS through the same
|
|
42
|
+
* JSON round trip. Callers whose logic needs live `Timestamp` semantics
|
|
43
|
+
* must not cache that read (see the pending-`publishSchedule` guards at
|
|
44
|
+
* the call sites).
|
|
45
|
+
*
|
|
46
|
+
* - NEGATIVES ARE NOT CACHED unless a caller opts in via `store`. A
|
|
47
|
+
* transient miss (host not found, doc missing) cached for the TTL is a
|
|
48
|
+
* self-inflicted outage; the Firestore negative-cache class of bug is
|
|
49
|
+
* exactly this shape.
|
|
50
|
+
*
|
|
51
|
+
* - OUTSIDE NEXT (jest, scripts) there is no incremental cache and
|
|
52
|
+
* `unstable_cache` throws its `incrementalCache missing` invariant before
|
|
53
|
+
* running the callback. The wrapper catches precisely that and falls back
|
|
54
|
+
* to the uncached read, so shared-lib specs exercise the same code path
|
|
55
|
+
* they always did.
|
|
56
|
+
*
|
|
57
|
+
* - NEVER cache per-user reads through this. Everything cached here must be
|
|
58
|
+
* a pure function of published site data.
|
|
59
|
+
*/ /**
|
|
60
|
+
* One tag per host covers every host-scoped cached doc — the host doc, the
|
|
61
|
+
* compose bundle, plugin installs, the sitemap payload. The tenant
|
|
62
|
+
* `/api/revalidate` route busts it on publish (alongside its existing
|
|
63
|
+
* `revalidatePath` calls), so a publish is exactly as instant as AGL-1150
|
|
64
|
+
* made it; the TTL is only the backstop for writes that never announce
|
|
65
|
+
* themselves.
|
|
66
|
+
*/ export const tenantDataTag = (hostId)=>`tenant-data:${hostId}`;
|
|
67
|
+
/**
|
|
68
|
+
* The backstop TTL for published site data — every read that
|
|
69
|
+
* `revalidateTag(tenantDataTag(hostId))` busts on publish.
|
|
70
|
+
*
|
|
71
|
+
* WHY AN HOUR AND NOT A MINUTE (2026-08-26, from the Vercel cost review).
|
|
72
|
+
*
|
|
73
|
+
* Every cache in this layer shipped at 60s, and the page above them was
|
|
74
|
+
* `revalidate = 60` too. Setting the backstop to the same order as the
|
|
75
|
+
* request rate makes it the PRIMARY propagation mechanism rather than a
|
|
76
|
+
* backstop: the whole chain expired together, so a render a minute paid the
|
|
77
|
+
* full sequential Firestore walk — host, org billing, redirect rules, page
|
|
78
|
+
* resolution, screen, compose bundle, enrichers — and none of it was ever
|
|
79
|
+
* stale in a way anyone could observe, because the publish path had already
|
|
80
|
+
* busted the tag the moment the content changed.
|
|
81
|
+
*
|
|
82
|
+
* Measured on `/[host]/[scheme]/[[...slug]]`: 0.78 GB-hr across 635 invocations, a P75
|
|
83
|
+
* of 3.65s, and ~4.4 GB-s per render — the most expensive route per hit in
|
|
84
|
+
* the account, on a site with no real traffic yet.
|
|
85
|
+
*
|
|
86
|
+
* So the TTL goes back to being what `withRenderCache`'s own docstring says
|
|
87
|
+
* it is: "the backstop for writes that never announce themselves." An hour
|
|
88
|
+
* bounds an unannounced direct-to-Firestore edit. Everything that goes
|
|
89
|
+
* through the product announces itself and is busted in the same instant it
|
|
90
|
+
* always was, so publish latency is unchanged.
|
|
91
|
+
*
|
|
92
|
+
* ⛔ NOT for reads that change OUTSIDE a publish. Two deliberately keep their
|
|
93
|
+
* own short TTLs and must not be moved onto this one:
|
|
94
|
+
*
|
|
95
|
+
* - `ORG_BILLING_TTL_SECONDS` — a plan change happens in the console and
|
|
96
|
+
* nothing busts the tenant tag for it, so this TTL is the ONLY thing
|
|
97
|
+
* propagating an upgrade or a downgrade to the live site.
|
|
98
|
+
* - `HOST_ALIAS_TTL_SECONDS` / `HOST_DOC_TTL_SECONDS` — a custom domain
|
|
99
|
+
* attaching is the customer sitting there waiting for their site
|
|
100
|
+
* (AGL-2010), and the `cname--` alias entry expires only by TTL.
|
|
101
|
+
*
|
|
102
|
+
* Takedowns are unaffected either way: the middleware runs ahead of every
|
|
103
|
+
* cache here and carries its own memo, which is what
|
|
104
|
+
* `lockdown-middleware-propagation.spec.ts` measures.
|
|
105
|
+
*/ export const PUBLISHED_SITE_DATA_TTL_SECONDS = 3600;
|
|
106
|
+
/**
|
|
107
|
+
* The backstop for the one read a screen publish changes under an UNCHANGED
|
|
108
|
+
* cache key — the version pointer (AGL-2573).
|
|
109
|
+
*
|
|
110
|
+
* The hour above is argued on the premise that "everything that goes through
|
|
111
|
+
* the product announces itself". That premise held for eleven days in August
|
|
112
|
+
* and then did not: the tenant refused the announce and every publish on the
|
|
113
|
+
* platform fell back to this TTL. The announce is still the mechanism — no TTL
|
|
114
|
+
* can make a publish instant, because the page above these caches is ISR-held
|
|
115
|
+
* and does not re-run a loader to notice a fresher document — but a backstop
|
|
116
|
+
* only bounds a failure, and an hour is not a bound anyone would choose for
|
|
117
|
+
* "your published page is not the page you published".
|
|
118
|
+
*
|
|
119
|
+
* Applied to the POINTER alone, deliberately, because the pointer alone is
|
|
120
|
+
* what makes the hour expensive. `get-screen-version` is keyed by `versionId`,
|
|
121
|
+
* so the body of a newly published version misses on its own; the only thing
|
|
122
|
+
* standing between a visitor and it is the screen document that names which
|
|
123
|
+
* version is live. That document is one small read, and shortening it leaves
|
|
124
|
+
* the expensive reads — the compose bundle, the node trees — at the hour the
|
|
125
|
+
* cost review set.
|
|
126
|
+
*
|
|
127
|
+
* The principle rather than a tuned figure: **the pointer must never be staler
|
|
128
|
+
* than the HTML that reads it** — `<=` the tenant catch-all's ISR window
|
|
129
|
+
* (`apps/tenant/app/[host]/[scheme]/[[...slug]]/page.tsx`), which
|
|
130
|
+
* `publish-pointer-tracks-page-window.spec.ts` enforces. Above the window, the
|
|
131
|
+
* page regenerates and faithfully rebuilds itself from a stale pointer, which
|
|
132
|
+
* is the failure being closed.
|
|
133
|
+
*
|
|
134
|
+
* It matched that window exactly while the window was also 600s. It no longer
|
|
135
|
+
* does, and staying at 600 under an hour-long window is the deliberate choice
|
|
136
|
+
* (AGL-2690): the loader runs only when the page regenerates, so a shorter TTL
|
|
137
|
+
* here costs the same single read per regeneration — a miss instead of a hit —
|
|
138
|
+
* and buys a rebuild from data at most ten minutes old rather than up to an
|
|
139
|
+
* hour old. That difference only shows when the publish announce is down, and
|
|
140
|
+
* the announce has been down for eleven days once already.
|
|
141
|
+
*
|
|
142
|
+
* The read cost this adds is one document per page REGENERATION rather than
|
|
143
|
+
* one per hour — regenerations are already bounded by the ISR window, so the
|
|
144
|
+
* ceiling is the render rate and not the request rate. That is the price of
|
|
145
|
+
* the bound, and it is the cheapest read on the path.
|
|
146
|
+
*/ export const PUBLISH_POINTER_TTL_SECONDS = 600;
|
|
147
|
+
/**
|
|
148
|
+
* The alias→hostId resolution is tagged separately because it is the one
|
|
149
|
+
* cache keyed BEFORE the hostId is known. It only changes on subdomain
|
|
150
|
+
* rename or custom-domain (dis)connect, never on publish.
|
|
151
|
+
*/ export const tenantHostAliasTag = (alias)=>`tenant-host:${alias}`;
|
|
152
|
+
/**
|
|
153
|
+
* A value that must be RETURNED but not STORED (a negative result, or a doc
|
|
154
|
+
* carrying a pending publish schedule). Thrown out of the cached callback so
|
|
155
|
+
* `unstable_cache` never writes it, caught by the wrapper and unwrapped.
|
|
156
|
+
*/ let BypassStore = class BypassStore extends Error {
|
|
157
|
+
constructor(value){
|
|
158
|
+
super('render-cache bypass'), this.value = value;
|
|
159
|
+
this.name = 'RenderCacheBypass';
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const isMissingIncrementalCache = (error)=>error instanceof Error && error.message.includes('incrementalCache missing');
|
|
163
|
+
/**
|
|
164
|
+
* The same normalization the page props already go through (`JSON.parse(
|
|
165
|
+
* JSON.stringify(...))` at every loader exit), applied at the cache boundary
|
|
166
|
+
* so hits and misses hand back byte-identical shapes.
|
|
167
|
+
*/ export function toPlainJson(value) {
|
|
168
|
+
if (value === undefined || value === null) return value;
|
|
169
|
+
return JSON.parse(JSON.stringify(value));
|
|
170
|
+
}
|
|
171
|
+
export async function withRenderCache(options) {
|
|
172
|
+
const { key, revalidate, tags, read, store } = options;
|
|
173
|
+
const cached = unstable_cache(async ()=>{
|
|
174
|
+
const value = toPlainJson(await read());
|
|
175
|
+
if (value === undefined || store && !store(value)) {
|
|
176
|
+
throw new BypassStore(value);
|
|
177
|
+
}
|
|
178
|
+
return value;
|
|
179
|
+
}, [
|
|
180
|
+
...key
|
|
181
|
+
], {
|
|
182
|
+
revalidate,
|
|
183
|
+
tags: [
|
|
184
|
+
...tags
|
|
185
|
+
]
|
|
186
|
+
});
|
|
187
|
+
try {
|
|
188
|
+
return await cached();
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (error instanceof BypassStore) return error.value;
|
|
191
|
+
// No Next server context (jest, one-off scripts): the invariant throws
|
|
192
|
+
// before the callback runs, so reading directly is not a double read.
|
|
193
|
+
if (isMissingIncrementalCache(error)) return toPlainJson(await read());
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
//# sourceMappingURL=render-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/tenant/data/admin/src/lib/render-cache.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 { unstable_cache } from 'next/cache'\n\n/**\n * Cross-request caching for the tenant render path's Firestore reads\n * (AGL-1302, the caching half of AGL-1151).\n *\n * Every tenant SSR render fanned out to ~15–40 uncached Admin-SDK reads —\n * host resolution, org billing, screen + version, the layout chain, and the\n * whole compose bundle (components, variables, functions, datasets,\n * workflows, plugin installs). None of that data is per-visitor, and almost\n * all of it is identical across every path on a site, so a 50-route sweep\n * paid the same host-scoped reads 50 times over. Routine verification\n * traffic alone burned ~44K reads/day (87% of the free tier).\n *\n * This module is deliberately a THIN wrapper around `unstable_cache` rather\n * than a cache of its own, so invalidation stays Next's: entries are tagged\n * and the existing publish/revalidate path (`/api/revalidate`, AGL-1150)\n * busts them with `revalidateTag` the same moment it drops the page HTML.\n *\n * Ground rules encoded here, because `unstable_cache` enforces none of them:\n *\n * - VALUES ARE JSON. `unstable_cache` stores `JSON.stringify(result)`, so a\n * cache HIT returns plain data while a MISS would return live objects —\n * Firestore `Timestamp`s, most importantly, whose `.seconds` getter decays\n * to `_seconds` across the round trip. Serving two shapes for the same\n * read is how a schedule check works in testing and misfires in\n * production, so `withRenderCache` normalizes the MISS through the same\n * JSON round trip. Callers whose logic needs live `Timestamp` semantics\n * must not cache that read (see the pending-`publishSchedule` guards at\n * the call sites).\n *\n * - NEGATIVES ARE NOT CACHED unless a caller opts in via `store`. A\n * transient miss (host not found, doc missing) cached for the TTL is a\n * self-inflicted outage; the Firestore negative-cache class of bug is\n * exactly this shape.\n *\n * - OUTSIDE NEXT (jest, scripts) there is no incremental cache and\n * `unstable_cache` throws its `incrementalCache missing` invariant before\n * running the callback. The wrapper catches precisely that and falls back\n * to the uncached read, so shared-lib specs exercise the same code path\n * they always did.\n *\n * - NEVER cache per-user reads through this. Everything cached here must be\n * a pure function of published site data.\n */\n\n/**\n * One tag per host covers every host-scoped cached doc — the host doc, the\n * compose bundle, plugin installs, the sitemap payload. The tenant\n * `/api/revalidate` route busts it on publish (alongside its existing\n * `revalidatePath` calls), so a publish is exactly as instant as AGL-1150\n * made it; the TTL is only the backstop for writes that never announce\n * themselves.\n */\nexport const tenantDataTag = (hostId: string): string => `tenant-data:${hostId}`\n\n/**\n * The backstop TTL for published site data — every read that\n * `revalidateTag(tenantDataTag(hostId))` busts on publish.\n *\n * WHY AN HOUR AND NOT A MINUTE (2026-08-26, from the Vercel cost review).\n *\n * Every cache in this layer shipped at 60s, and the page above them was\n * `revalidate = 60` too. Setting the backstop to the same order as the\n * request rate makes it the PRIMARY propagation mechanism rather than a\n * backstop: the whole chain expired together, so a render a minute paid the\n * full sequential Firestore walk — host, org billing, redirect rules, page\n * resolution, screen, compose bundle, enrichers — and none of it was ever\n * stale in a way anyone could observe, because the publish path had already\n * busted the tag the moment the content changed.\n *\n * Measured on `/[host]/[scheme]/[[...slug]]`: 0.78 GB-hr across 635 invocations, a P75\n * of 3.65s, and ~4.4 GB-s per render — the most expensive route per hit in\n * the account, on a site with no real traffic yet.\n *\n * So the TTL goes back to being what `withRenderCache`'s own docstring says\n * it is: \"the backstop for writes that never announce themselves.\" An hour\n * bounds an unannounced direct-to-Firestore edit. Everything that goes\n * through the product announces itself and is busted in the same instant it\n * always was, so publish latency is unchanged.\n *\n * ⛔ NOT for reads that change OUTSIDE a publish. Two deliberately keep their\n * own short TTLs and must not be moved onto this one:\n *\n * - `ORG_BILLING_TTL_SECONDS` — a plan change happens in the console and\n * nothing busts the tenant tag for it, so this TTL is the ONLY thing\n * propagating an upgrade or a downgrade to the live site.\n * - `HOST_ALIAS_TTL_SECONDS` / `HOST_DOC_TTL_SECONDS` — a custom domain\n * attaching is the customer sitting there waiting for their site\n * (AGL-2010), and the `cname--` alias entry expires only by TTL.\n *\n * Takedowns are unaffected either way: the middleware runs ahead of every\n * cache here and carries its own memo, which is what\n * `lockdown-middleware-propagation.spec.ts` measures.\n */\nexport const PUBLISHED_SITE_DATA_TTL_SECONDS = 3600\n\n/**\n * The backstop for the one read a screen publish changes under an UNCHANGED\n * cache key — the version pointer (AGL-2573).\n *\n * The hour above is argued on the premise that \"everything that goes through\n * the product announces itself\". That premise held for eleven days in August\n * and then did not: the tenant refused the announce and every publish on the\n * platform fell back to this TTL. The announce is still the mechanism — no TTL\n * can make a publish instant, because the page above these caches is ISR-held\n * and does not re-run a loader to notice a fresher document — but a backstop\n * only bounds a failure, and an hour is not a bound anyone would choose for\n * \"your published page is not the page you published\".\n *\n * Applied to the POINTER alone, deliberately, because the pointer alone is\n * what makes the hour expensive. `get-screen-version` is keyed by `versionId`,\n * so the body of a newly published version misses on its own; the only thing\n * standing between a visitor and it is the screen document that names which\n * version is live. That document is one small read, and shortening it leaves\n * the expensive reads — the compose bundle, the node trees — at the hour the\n * cost review set.\n *\n * The principle rather than a tuned figure: **the pointer must never be staler\n * than the HTML that reads it** — `<=` the tenant catch-all's ISR window\n * (`apps/tenant/app/[host]/[scheme]/[[...slug]]/page.tsx`), which\n * `publish-pointer-tracks-page-window.spec.ts` enforces. Above the window, the\n * page regenerates and faithfully rebuilds itself from a stale pointer, which\n * is the failure being closed.\n *\n * It matched that window exactly while the window was also 600s. It no longer\n * does, and staying at 600 under an hour-long window is the deliberate choice\n * (AGL-2690): the loader runs only when the page regenerates, so a shorter TTL\n * here costs the same single read per regeneration — a miss instead of a hit —\n * and buys a rebuild from data at most ten minutes old rather than up to an\n * hour old. That difference only shows when the publish announce is down, and\n * the announce has been down for eleven days once already.\n *\n * The read cost this adds is one document per page REGENERATION rather than\n * one per hour — regenerations are already bounded by the ISR window, so the\n * ceiling is the render rate and not the request rate. That is the price of\n * the bound, and it is the cheapest read on the path.\n */\nexport const PUBLISH_POINTER_TTL_SECONDS = 600\n\n/**\n * The alias→hostId resolution is tagged separately because it is the one\n * cache keyed BEFORE the hostId is known. It only changes on subdomain\n * rename or custom-domain (dis)connect, never on publish.\n */\nexport const tenantHostAliasTag = (alias: string): string =>\n `tenant-host:${alias}`\n\n/**\n * A value that must be RETURNED but not STORED (a negative result, or a doc\n * carrying a pending publish schedule). Thrown out of the cached callback so\n * `unstable_cache` never writes it, caught by the wrapper and unwrapped.\n */\nclass BypassStore extends Error {\n constructor(readonly value: unknown) {\n super('render-cache bypass')\n this.name = 'RenderCacheBypass'\n }\n}\n\nconst isMissingIncrementalCache = (error: unknown): boolean =>\n error instanceof Error && error.message.includes('incrementalCache missing')\n\n/**\n * The same normalization the page props already go through (`JSON.parse(\n * JSON.stringify(...))` at every loader exit), applied at the cache boundary\n * so hits and misses hand back byte-identical shapes.\n */\nexport function toPlainJson<T>(value: T): T {\n if (value === undefined || value === null) return value\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nexport interface RenderCacheOptions<T> {\n /**\n * Cache key parts. The FIRST part must be unique per read helper — every\n * call funnels through the same callback source, and `unstable_cache` keys\n * on callback text + key parts, so the name prefix is what keeps\n * `getScreen` and `getComponents` from colliding.\n */\n key: readonly string[]\n /** TTL in seconds — the staleness backstop when no tag bust arrives. */\n revalidate: number\n /** Tags `revalidateTag` can bust — see `tenantDataTag`. */\n tags: readonly string[]\n /** The uncached read. Must resolve to JSON-serializable data. */\n read: () => Promise<T>\n /**\n * Return `false` to serve the value WITHOUT storing it. Negative results\n * and docs whose live `Timestamp` semantics matter (pending publish\n * schedules) belong here. `undefined` is never stored regardless.\n */\n store?: (value: T) => boolean\n}\n\nexport async function withRenderCache<T>(\n options: RenderCacheOptions<T>,\n): Promise<T> {\n const { key, revalidate, tags, read, store } = options\n const cached = unstable_cache(\n async () => {\n const value = toPlainJson(await read())\n if (value === undefined || (store && !store(value))) {\n throw new BypassStore(value)\n }\n return value\n },\n [...key],\n { revalidate, tags: [...tags] },\n )\n try {\n return await cached()\n } catch (error) {\n if (error instanceof BypassStore) return error.value as T\n // No Next server context (jest, one-off scripts): the invariant throws\n // before the callback runs, so reading directly is not a double read.\n if (isMissingIncrementalCache(error)) return toPlainJson(await read())\n throw error\n }\n}\n"],"names":["unstable_cache","tenantDataTag","hostId","PUBLISHED_SITE_DATA_TTL_SECONDS","PUBLISH_POINTER_TTL_SECONDS","tenantHostAliasTag","alias","BypassStore","Error","value","name","isMissingIncrementalCache","error","message","includes","toPlainJson","undefined","JSON","parse","stringify","withRenderCache","options","key","revalidate","tags","read","store","cached"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,cAAc,QAAQ,aAAY;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CC,GAED;;;;;;;CAOC,GACD,OAAO,MAAMC,gBAAgB,CAACC,SAA2B,CAAC,YAAY,EAAEA,QAAQ,CAAA;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsCC,GACD,OAAO,MAAMC,kCAAkC,KAAI;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwCC,GACD,OAAO,MAAMC,8BAA8B,IAAG;AAE9C;;;;CAIC,GACD,OAAO,MAAMC,qBAAqB,CAACC,QACjC,CAAC,YAAY,EAAEA,OAAO,CAAA;AAExB;;;;CAIC,GACD,IAAA,AAAMC,cAAN,MAAMA,oBAAoBC;IACxB,YAAY,AAASC,KAAc,CAAE;QACnC,KAAK,CAAC,6BADaA,QAAAA;QAEnB,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAEA,MAAMC,4BAA4B,CAACC,QACjCA,iBAAiBJ,SAASI,MAAMC,OAAO,CAACC,QAAQ,CAAC;AAEnD;;;;CAIC,GACD,OAAO,SAASC,YAAeN,KAAQ;IACrC,IAAIA,UAAUO,aAAaP,UAAU,MAAM,OAAOA;IAClD,OAAOQ,KAAKC,KAAK,CAACD,KAAKE,SAAS,CAACV;AACnC;AAwBA,OAAO,eAAeW,gBACpBC,OAA8B;IAE9B,MAAM,EAAEC,GAAG,EAAEC,UAAU,EAAEC,IAAI,EAAEC,IAAI,EAAEC,KAAK,EAAE,GAAGL;IAC/C,MAAMM,SAAS3B,eACb;QACE,MAAMS,QAAQM,YAAY,MAAMU;QAChC,IAAIhB,UAAUO,aAAcU,SAAS,CAACA,MAAMjB,QAAS;YACnD,MAAM,IAAIF,YAAYE;QACxB;QACA,OAAOA;IACT,GACA;WAAIa;KAAI,EACR;QAAEC;QAAYC,MAAM;eAAIA;SAAK;IAAC;IAEhC,IAAI;QACF,OAAO,MAAMG;IACf,EAAE,OAAOf,OAAO;QACd,IAAIA,iBAAiBL,aAAa,OAAOK,MAAMH,KAAK;QACpD,uEAAuE;QACvE,sEAAsE;QACtE,IAAIE,0BAA0BC,QAAQ,OAAOG,YAAY,MAAMU;QAC/D,MAAMb;IACR;AACF"}
|
|
@@ -0,0 +1,160 @@
|
|
|
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
|
+
/** Where an address came from. An address can have more than one. */
|
|
18
|
+
export type AccountAddressSource =
|
|
19
|
+
/** The Firebase Auth record's `email` — what a token carries today. */
|
|
20
|
+
'primary'
|
|
21
|
+
/** A federated provider's own address, from `providerData[].email`. */
|
|
22
|
+
| 'provider'
|
|
23
|
+
/** A row in `users/{uid}/emails` — a confirmed alias, or a former primary. */
|
|
24
|
+
| 'stored';
|
|
25
|
+
/** One address, with everything a consumer needs to decide what to do. */
|
|
26
|
+
export interface AccountAddress {
|
|
27
|
+
/** Lowercased and trimmed. The form both hashed stores key on. */
|
|
28
|
+
address: string;
|
|
29
|
+
/** Every source that named it, so a card can say WHY it is listed. */
|
|
30
|
+
sources: AccountAddressSource[];
|
|
31
|
+
/**
|
|
32
|
+
* `sha256(address)` — the SAME derivation `emailSuppressionKey` uses, so a
|
|
33
|
+
* consumer never has to re-derive it and the two can never disagree about
|
|
34
|
+
* which document describes which person.
|
|
35
|
+
*/
|
|
36
|
+
key: string;
|
|
37
|
+
/**
|
|
38
|
+
* Another account holds this address too.
|
|
39
|
+
*
|
|
40
|
+
* ⚠️ `false` means "nothing found", NOT "nobody else holds it" — see the
|
|
41
|
+
* reverse-direction note in this module's header.
|
|
42
|
+
*/
|
|
43
|
+
shared: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* A provider asserted this address for this account and the uniqueness
|
|
46
|
+
* claim was REFUSED, because another account already held it.
|
|
47
|
+
*
|
|
48
|
+
* Carried onto the staff surface rather than left in a log line. The
|
|
49
|
+
* refusal is the whole mechanism: silently skipping the claim is what let
|
|
50
|
+
* the live collision exist unrecorded, so an account carrying a provider
|
|
51
|
+
* address it does not own has to be visible on its own page.
|
|
52
|
+
*/
|
|
53
|
+
indexConflict: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* ## How big this can get, and why nothing here slices
|
|
57
|
+
*
|
|
58
|
+
* `MAX_ACCOUNT_EMAILS` is 5 and caps the SUBCOLLECTION only; the primary and
|
|
59
|
+
* the provider addresses are additional, so the set can exceed 5. It stays
|
|
60
|
+
* far under Firestore's 30-element `in` limit in every realistic shape, but
|
|
61
|
+
* "far under" is not a guarantee and a truncated erasure is the worst outcome
|
|
62
|
+
* available in this whole area — it reports success over addresses it never
|
|
63
|
+
* swept.
|
|
64
|
+
*
|
|
65
|
+
* So the rule is: consumers ITERATE this list, they do not slice it, and any
|
|
66
|
+
* consumer feeding it to an `in` query CHUNKS. No function in this module
|
|
67
|
+
* truncates its result.
|
|
68
|
+
*/
|
|
69
|
+
export interface AccountAddressSet {
|
|
70
|
+
uid: string;
|
|
71
|
+
/** The Auth record's current address, or null for an addressless account. */
|
|
72
|
+
primary: string | null;
|
|
73
|
+
/** Primary first, then the rest in a stable order. */
|
|
74
|
+
addresses: AccountAddress[];
|
|
75
|
+
/**
|
|
76
|
+
* A source threw and the set may be SHORT.
|
|
77
|
+
*
|
|
78
|
+
* Load-bearing for erasure: erasing "every address" from an incomplete set
|
|
79
|
+
* is not erasing every address, and the caller has to be able to say so
|
|
80
|
+
* rather than report a complete erasure it did not perform.
|
|
81
|
+
*/
|
|
82
|
+
incomplete: boolean;
|
|
83
|
+
}
|
|
84
|
+
/** `sha256` of the normalized address. */
|
|
85
|
+
export declare function accountAddressKey(address: string): string;
|
|
86
|
+
/**
|
|
87
|
+
* Every address `uid` holds, from all three sources.
|
|
88
|
+
*
|
|
89
|
+
* `record` is passed in rather than fetched: every caller already has the
|
|
90
|
+
* Auth record in hand, and re-fetching it would add a pooled lookup to a page
|
|
91
|
+
* that has already paid for one.
|
|
92
|
+
*
|
|
93
|
+
* Best-effort per source. A Firestore outage must not make the delivery card
|
|
94
|
+
* fall back to the single-address behaviour SILENTLY — it reports
|
|
95
|
+
* `incomplete` instead, and the callers that destroy data refuse on it.
|
|
96
|
+
*
|
|
97
|
+
* @param detectShared consult the reverse direction for each non-primary
|
|
98
|
+
* address. One extra lookup per address; skip it where the answer is
|
|
99
|
+
* not used.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resolveAccountAddresses(options: {
|
|
102
|
+
uid: string;
|
|
103
|
+
/** The Auth record — `email` and `providerData` are read from it. */
|
|
104
|
+
record?: {
|
|
105
|
+
email?: string | null;
|
|
106
|
+
providerData?: readonly {
|
|
107
|
+
email?: string | null;
|
|
108
|
+
}[];
|
|
109
|
+
} | null;
|
|
110
|
+
detectShared?: boolean;
|
|
111
|
+
firestore?: any;
|
|
112
|
+
}): Promise<AccountAddressSet>;
|
|
113
|
+
/** Just the addresses, for a caller that needs nothing else. */
|
|
114
|
+
export declare function addressList(set: AccountAddressSet): string[];
|
|
115
|
+
/** Just the hashed keys, for a query against a store keyed by them. */
|
|
116
|
+
export declare function addressKeys(set: AccountAddressSet): string[];
|
|
117
|
+
export interface AddressHolders {
|
|
118
|
+
address: string;
|
|
119
|
+
/** Every uid found to hold it. May be SHORT — see below. */
|
|
120
|
+
uids: string[];
|
|
121
|
+
/** More than one distinct account was found. */
|
|
122
|
+
ambiguous: boolean;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Which accounts hold `address`, as far as can be determined.
|
|
126
|
+
*
|
|
127
|
+
* ## What it can and cannot see
|
|
128
|
+
*
|
|
129
|
+
* It consults the two sources an address can be looked up IN:
|
|
130
|
+
*
|
|
131
|
+
* - Firebase Auth, across pools — the account holding it as its PRIMARY.
|
|
132
|
+
* - `emailIdentityIndex/{address}` — the account holding it as a CONFIRMED
|
|
133
|
+
* alias.
|
|
134
|
+
*
|
|
135
|
+
* It cannot see a `providerData` holder, because Firebase Auth offers no
|
|
136
|
+
* lookup by provider address; that field is readable only from a record you
|
|
137
|
+
* already have. This is not a gap that can be closed by trying harder, and
|
|
138
|
+
* the live shape of it is ordinary: an account whose Google provider address
|
|
139
|
+
* is another account's primary.
|
|
140
|
+
*
|
|
141
|
+
* So the result is sound in ONE direction only. `ambiguous: true` is proof
|
|
142
|
+
* that more than one account holds the address. `ambiguous: false` is the
|
|
143
|
+
* absence of evidence, never evidence of absence — a caller must not treat a
|
|
144
|
+
* single uid as "this address belongs to this person".
|
|
145
|
+
*/
|
|
146
|
+
export declare function findAccountsHoldingAddress(input: unknown, options?: {
|
|
147
|
+
firestore?: any;
|
|
148
|
+
}): Promise<AddressHolders>;
|
|
149
|
+
/**
|
|
150
|
+
* The single account an address can be attributed to, or null.
|
|
151
|
+
*
|
|
152
|
+
* Null for BOTH "nobody" and "more than one", deliberately. The caller writes
|
|
153
|
+
* an audit subject with it, and a row naming the wrong customer is worse than
|
|
154
|
+
* a row naming none: the collection exists to answer "who at Aglyn read my
|
|
155
|
+
* data", and an answer that points at an innocent account is not a weaker
|
|
156
|
+
* answer, it is a false one.
|
|
157
|
+
*/
|
|
158
|
+
export declare function attributableAccountForAddress(input: unknown, options?: {
|
|
159
|
+
firestore?: any;
|
|
160
|
+
}): Promise<string | null>;
|