@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,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The lockdown gate for VISITOR writes on a customer's live site
|
|
19
|
+
* (AGL-1511) — form submissions, cart changes, checkout.
|
|
20
|
+
*
|
|
21
|
+
* Why this is its own chokepoint rather than the console's. The tenant
|
|
22
|
+
* middleware, which is where AGL-1501 enforces, **does not match `/api`**
|
|
23
|
+
* (see the matcher in apps/tenant/middleware.ts): it exists to stop CACHED
|
|
24
|
+
* PAGES serving. So the site's own write endpoints have had no lockdown
|
|
25
|
+
* enforcement at all — under a full org takedown the pages 503 while a form
|
|
26
|
+
* POST still writes. This closes that for both modes.
|
|
27
|
+
*
|
|
28
|
+
* The product decision this file encodes, from the issue: a visitor-facing
|
|
29
|
+
* write on a read-serving site gets a **polite inline pause, never a
|
|
30
|
+
* page-level 503**. A customer's site staying up and earning is the entire
|
|
31
|
+
* reason read-only mode exists instead of full lockdown, and answering their
|
|
32
|
+
* visitors with an outage page would spend exactly what the mode was
|
|
33
|
+
* protecting. So the refusal is a 423 with visitor copy the block renders
|
|
34
|
+
* inline, and the page around it never stops rendering.
|
|
35
|
+
*
|
|
36
|
+
* Under a FULL lock the same 423 is returned rather than a 503, and that is
|
|
37
|
+
* deliberate too: the middleware has already replaced the whole page for
|
|
38
|
+
* anyone loading it, so a write arriving here is a stale tab or a script,
|
|
39
|
+
* and a machine-readable refusal is more useful to both than an HTML page.
|
|
40
|
+
*
|
|
41
|
+
* NO staff bypass and NO user scope, matching /api/lockdown-verdict: this is
|
|
42
|
+
* a public website, the caller is anonymous, and a visitor's identity plays
|
|
43
|
+
* no part in whether a site accepts an order.
|
|
44
|
+
*/
|
|
45
|
+
import { type LockdownIntent, type LockdownPausedSurface, type LockdownState } from '@aglyn/aglyn/server';
|
|
46
|
+
/**
|
|
47
|
+
* The active platform/org/host lockdown for one site, or null.
|
|
48
|
+
*
|
|
49
|
+
* Both reads are already paid by the callers on this path — the dispatcher
|
|
50
|
+
* reads the host doc for its plugin deny-list and the org doc for
|
|
51
|
+
* enablement, and both getters are `React.cache`-deduped per request — so
|
|
52
|
+
* the gate costs one TTL-cached platform read and nothing else.
|
|
53
|
+
*
|
|
54
|
+
* FAIL OPEN on any error, matching every other lockdown reader: an
|
|
55
|
+
* unreachable Firestore is an outage, not a lockdown, and a shop that stops
|
|
56
|
+
* taking orders because a read timed out is a worse failure than the one
|
|
57
|
+
* being guarded against.
|
|
58
|
+
*
|
|
59
|
+
* …EXCEPT for a takedown (AGL-1881, extending AGL-1621). One class of lock
|
|
60
|
+
* inverts that cost function — "the database was down" is not an answer to a
|
|
61
|
+
* court order — and until now the ledger that carries it covered only the
|
|
62
|
+
* four scopes whose lock lives in `lockdowns/*`. Org and host locks live on
|
|
63
|
+
* the org/host document, so the two scopes MOST likely to be carrying a
|
|
64
|
+
* legal order were the two that fell through. Every successful read now
|
|
65
|
+
* records its org and host verdict, and a failed read serves back whichever
|
|
66
|
+
* of them was a takedown and is still active.
|
|
67
|
+
*
|
|
68
|
+
* Note what is NOT done here: the catch does not fail closed on the state it
|
|
69
|
+
* could not read. On a read failure `suspendedEnforcement` is exactly as
|
|
70
|
+
* unreadable as everything else, which is the whole reason a separate
|
|
71
|
+
* durable ledger exists — and a catch that locked unconditionally would take
|
|
72
|
+
* every shop on the platform offline on any transient blip, which is the
|
|
73
|
+
* deliberate posture this keeps for the default case.
|
|
74
|
+
*/
|
|
75
|
+
export declare function getSiteLockdown(hostId: string, nowMs?: number): Promise<LockdownState | null>;
|
|
76
|
+
/**
|
|
77
|
+
* The refusal a visitor-facing write route returns, or null to proceed:
|
|
78
|
+
*
|
|
79
|
+
* const paused = await visitorWriteRefusal({ hostId, request, surface })
|
|
80
|
+
* if (paused) return paused
|
|
81
|
+
*
|
|
82
|
+
* `surface` picks the WORDS only (`lockdownPausedNotice`) — the checkout
|
|
83
|
+
* sentence has to promise no card was charged, and no generic copy can.
|
|
84
|
+
*/
|
|
85
|
+
export declare function visitorWriteRefusal(options: {
|
|
86
|
+
hostId: string;
|
|
87
|
+
/** The request whose method decides read vs write. */
|
|
88
|
+
request?: {
|
|
89
|
+
method?: string;
|
|
90
|
+
} | null;
|
|
91
|
+
/** Overrides `request`; `write` when neither is given. */
|
|
92
|
+
intent?: LockdownIntent;
|
|
93
|
+
surface: LockdownPausedSurface;
|
|
94
|
+
nowMs?: number;
|
|
95
|
+
}): Promise<Response | null>;
|
|
96
|
+
/**
|
|
97
|
+
* The refusal a visitor-facing route that SERVES SITE CONTENT over `/api`
|
|
98
|
+
* returns under a FULL lock, or null to proceed (AGL-2495):
|
|
99
|
+
*
|
|
100
|
+
* const down = await visitorContentRefusal({ hostId })
|
|
101
|
+
* if (down) return down
|
|
102
|
+
*
|
|
103
|
+
* The gap this closes, found by the AGL-1621 drill. The tenant middleware
|
|
104
|
+
* is what takes a locked site off the air, and its matcher deliberately
|
|
105
|
+
* excludes `/api` — so a full org or host takedown 503s every page while an
|
|
106
|
+
* API route that composes and returns screen nodes keeps handing the site's
|
|
107
|
+
* content out to anyone who can name a screen. `visitorWriteRefusal` above
|
|
108
|
+
* cannot answer that: it leaves before any read, by design, because it is a
|
|
109
|
+
* WRITE gate on the hot unauthenticated GET path.
|
|
110
|
+
*
|
|
111
|
+
* FULL locks only, and that is the whole point of it being a separate
|
|
112
|
+
* helper rather than a flag on the write gate. Read-only mode exists so a
|
|
113
|
+
* customer's site keeps serving and earning; refusing reads there would
|
|
114
|
+
* spend exactly what the mode protects. `lockdownBlocks(state, 'read')` is
|
|
115
|
+
* true only when the mode is full, which is precisely the case where the
|
|
116
|
+
* pages are already gone.
|
|
117
|
+
*
|
|
118
|
+
* Deliberately NOT applied to the whole tenant API surface. A read-refusing
|
|
119
|
+
* gate on every route would take the lock notice, the verdict probe and the
|
|
120
|
+
* abuse intake down with the site. This is for the routes that serve the
|
|
121
|
+
* SITE ITSELF — a composed node tree — where continuing to serve is
|
|
122
|
+
* continuing to publish. `serve-media-cdn.ts` made the same call for bytes
|
|
123
|
+
* (AGL-1520); this is the node-tree half of it.
|
|
124
|
+
*
|
|
125
|
+
* Fails OPEN through `getSiteLockdown`, like every other reader here.
|
|
126
|
+
*/
|
|
127
|
+
export declare function visitorContentRefusal(options: {
|
|
128
|
+
hostId: string;
|
|
129
|
+
surface?: LockdownPausedSurface;
|
|
130
|
+
nowMs?: number;
|
|
131
|
+
}): Promise<Response | null>;
|
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
* The lockdown gate for VISITOR writes on a customer's live site
|
|
18
|
+
* (AGL-1511) — form submissions, cart changes, checkout.
|
|
19
|
+
*
|
|
20
|
+
* Why this is its own chokepoint rather than the console's. The tenant
|
|
21
|
+
* middleware, which is where AGL-1501 enforces, **does not match `/api`**
|
|
22
|
+
* (see the matcher in apps/tenant/middleware.ts): it exists to stop CACHED
|
|
23
|
+
* PAGES serving. So the site's own write endpoints have had no lockdown
|
|
24
|
+
* enforcement at all — under a full org takedown the pages 503 while a form
|
|
25
|
+
* POST still writes. This closes that for both modes.
|
|
26
|
+
*
|
|
27
|
+
* The product decision this file encodes, from the issue: a visitor-facing
|
|
28
|
+
* write on a read-serving site gets a **polite inline pause, never a
|
|
29
|
+
* page-level 503**. A customer's site staying up and earning is the entire
|
|
30
|
+
* reason read-only mode exists instead of full lockdown, and answering their
|
|
31
|
+
* visitors with an outage page would spend exactly what the mode was
|
|
32
|
+
* protecting. So the refusal is a 423 with visitor copy the block renders
|
|
33
|
+
* inline, and the page around it never stops rendering.
|
|
34
|
+
*
|
|
35
|
+
* Under a FULL lock the same 423 is returned rather than a 503, and that is
|
|
36
|
+
* deliberate too: the middleware has already replaced the whole page for
|
|
37
|
+
* anyone loading it, so a write arriving here is a stale tab or a script,
|
|
38
|
+
* and a machine-readable refusal is more useful to both than an HTML page.
|
|
39
|
+
*
|
|
40
|
+
* NO staff bypass and NO user scope, matching /api/lockdown-verdict: this is
|
|
41
|
+
* a public website, the caller is anonymous, and a visitor's identity plays
|
|
42
|
+
* no part in whether a site accepts an order.
|
|
43
|
+
*/ import { isLockdownActive, lockdownBlocks, lockdownIntentForMethod, lockdownPausedNotice, normalizeHostLockdown, normalizeOrgLockdown, resolveLockdown } from "@aglyn/aglyn/server";
|
|
44
|
+
import { getPlatformLockdown, heldSiteTakedown, lockdownJsonResponse, rememberSiteTakedown } from "./lockdown.js";
|
|
45
|
+
import { getHostDocAdmin, getOrgForHost } from "./organizations.js";
|
|
46
|
+
/**
|
|
47
|
+
* The active platform/org/host lockdown for one site, or null.
|
|
48
|
+
*
|
|
49
|
+
* Both reads are already paid by the callers on this path — the dispatcher
|
|
50
|
+
* reads the host doc for its plugin deny-list and the org doc for
|
|
51
|
+
* enablement, and both getters are `React.cache`-deduped per request — so
|
|
52
|
+
* the gate costs one TTL-cached platform read and nothing else.
|
|
53
|
+
*
|
|
54
|
+
* FAIL OPEN on any error, matching every other lockdown reader: an
|
|
55
|
+
* unreachable Firestore is an outage, not a lockdown, and a shop that stops
|
|
56
|
+
* taking orders because a read timed out is a worse failure than the one
|
|
57
|
+
* being guarded against.
|
|
58
|
+
*
|
|
59
|
+
* …EXCEPT for a takedown (AGL-1881, extending AGL-1621). One class of lock
|
|
60
|
+
* inverts that cost function — "the database was down" is not an answer to a
|
|
61
|
+
* court order — and until now the ledger that carries it covered only the
|
|
62
|
+
* four scopes whose lock lives in `lockdowns/*`. Org and host locks live on
|
|
63
|
+
* the org/host document, so the two scopes MOST likely to be carrying a
|
|
64
|
+
* legal order were the two that fell through. Every successful read now
|
|
65
|
+
* records its org and host verdict, and a failed read serves back whichever
|
|
66
|
+
* of them was a takedown and is still active.
|
|
67
|
+
*
|
|
68
|
+
* Note what is NOT done here: the catch does not fail closed on the state it
|
|
69
|
+
* could not read. On a read failure `suspendedEnforcement` is exactly as
|
|
70
|
+
* unreadable as everything else, which is the whole reason a separate
|
|
71
|
+
* durable ledger exists — and a catch that locked unconditionally would take
|
|
72
|
+
* every shop on the platform offline on any transient blip, which is the
|
|
73
|
+
* deliberate posture this keeps for the default case.
|
|
74
|
+
*/ export async function getSiteLockdown(hostId, nowMs = Date.now()) {
|
|
75
|
+
if (!hostId) return null;
|
|
76
|
+
try {
|
|
77
|
+
const [platform, resolved, host] = await Promise.all([
|
|
78
|
+
getPlatformLockdown(),
|
|
79
|
+
getOrgForHost(hostId),
|
|
80
|
+
getHostDocAdmin(hostId)
|
|
81
|
+
]);
|
|
82
|
+
const org = normalizeOrgLockdown(resolved == null ? void 0 : resolved.org);
|
|
83
|
+
const hostState = normalizeHostLockdown(host);
|
|
84
|
+
rememberSiteTakedown(hostId, {
|
|
85
|
+
org,
|
|
86
|
+
host: hostState
|
|
87
|
+
});
|
|
88
|
+
const state = resolveLockdown({
|
|
89
|
+
platform,
|
|
90
|
+
org,
|
|
91
|
+
host: hostState
|
|
92
|
+
}, nowMs);
|
|
93
|
+
return isLockdownActive(state, nowMs) ? state : null;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('[tenant-write-lockdown] verdict failed', hostId, error);
|
|
96
|
+
const held = heldSiteTakedown(hostId, nowMs);
|
|
97
|
+
// The shipped fail-open answer, returned by the same statement it always
|
|
98
|
+
// was, for every case where nothing takedown-class was ever observed.
|
|
99
|
+
if (!held.org && !held.host) return null;
|
|
100
|
+
// The platform read is TTL-cached and swallows its own errors (it carries
|
|
101
|
+
// its own ledger entry), so asking again here costs nothing and keeps a
|
|
102
|
+
// wider lock from being masked by a narrower held one.
|
|
103
|
+
const platform = await getPlatformLockdown().catch(()=>null);
|
|
104
|
+
const state = resolveLockdown({
|
|
105
|
+
platform,
|
|
106
|
+
org: held.org,
|
|
107
|
+
host: held.host
|
|
108
|
+
}, nowMs);
|
|
109
|
+
return isLockdownActive(state, nowMs) ? state : null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The refusal a visitor-facing write route returns, or null to proceed:
|
|
114
|
+
*
|
|
115
|
+
* const paused = await visitorWriteRefusal({ hostId, request, surface })
|
|
116
|
+
* if (paused) return paused
|
|
117
|
+
*
|
|
118
|
+
* `surface` picks the WORDS only (`lockdownPausedNotice`) — the checkout
|
|
119
|
+
* sentence has to promise no card was charged, and no generic copy can.
|
|
120
|
+
*/ export async function visitorWriteRefusal(options) {
|
|
121
|
+
var _options_nowMs, _options_intent;
|
|
122
|
+
const nowMs = (_options_nowMs = options.nowMs) != null ? _options_nowMs : Date.now();
|
|
123
|
+
const intent = (_options_intent = options.intent) != null ? _options_intent : options.request ? lockdownIntentForMethod(options.request.method) : 'write';
|
|
124
|
+
// This is a WRITE gate: reads leave before any Firestore read, so the hot
|
|
125
|
+
// unauthenticated GET path pays nothing. A full lock's refusal of tenant
|
|
126
|
+
// API *reads* is not attempted here and is unchanged from today — the
|
|
127
|
+
// middleware owns the page surface, and a read-refusing API gate is its
|
|
128
|
+
// own decision with its own blast radius (filed separately).
|
|
129
|
+
if (intent === 'read') return null;
|
|
130
|
+
const state = await getSiteLockdown(options.hostId, nowMs);
|
|
131
|
+
if (!lockdownBlocks(state, intent)) return null;
|
|
132
|
+
return lockdownJsonResponse(state, {
|
|
133
|
+
notice: lockdownPausedNotice(options.surface)
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The refusal a visitor-facing route that SERVES SITE CONTENT over `/api`
|
|
138
|
+
* returns under a FULL lock, or null to proceed (AGL-2495):
|
|
139
|
+
*
|
|
140
|
+
* const down = await visitorContentRefusal({ hostId })
|
|
141
|
+
* if (down) return down
|
|
142
|
+
*
|
|
143
|
+
* The gap this closes, found by the AGL-1621 drill. The tenant middleware
|
|
144
|
+
* is what takes a locked site off the air, and its matcher deliberately
|
|
145
|
+
* excludes `/api` — so a full org or host takedown 503s every page while an
|
|
146
|
+
* API route that composes and returns screen nodes keeps handing the site's
|
|
147
|
+
* content out to anyone who can name a screen. `visitorWriteRefusal` above
|
|
148
|
+
* cannot answer that: it leaves before any read, by design, because it is a
|
|
149
|
+
* WRITE gate on the hot unauthenticated GET path.
|
|
150
|
+
*
|
|
151
|
+
* FULL locks only, and that is the whole point of it being a separate
|
|
152
|
+
* helper rather than a flag on the write gate. Read-only mode exists so a
|
|
153
|
+
* customer's site keeps serving and earning; refusing reads there would
|
|
154
|
+
* spend exactly what the mode protects. `lockdownBlocks(state, 'read')` is
|
|
155
|
+
* true only when the mode is full, which is precisely the case where the
|
|
156
|
+
* pages are already gone.
|
|
157
|
+
*
|
|
158
|
+
* Deliberately NOT applied to the whole tenant API surface. A read-refusing
|
|
159
|
+
* gate on every route would take the lock notice, the verdict probe and the
|
|
160
|
+
* abuse intake down with the site. This is for the routes that serve the
|
|
161
|
+
* SITE ITSELF — a composed node tree — where continuing to serve is
|
|
162
|
+
* continuing to publish. `serve-media-cdn.ts` made the same call for bytes
|
|
163
|
+
* (AGL-1520); this is the node-tree half of it.
|
|
164
|
+
*
|
|
165
|
+
* Fails OPEN through `getSiteLockdown`, like every other reader here.
|
|
166
|
+
*/ export async function visitorContentRefusal(options) {
|
|
167
|
+
var _options_nowMs;
|
|
168
|
+
const nowMs = (_options_nowMs = options.nowMs) != null ? _options_nowMs : Date.now();
|
|
169
|
+
const state = await getSiteLockdown(options.hostId, nowMs);
|
|
170
|
+
if (!lockdownBlocks(state, 'read')) return null;
|
|
171
|
+
return lockdownJsonResponse(state, options.surface ? {
|
|
172
|
+
notice: lockdownPausedNotice(options.surface)
|
|
173
|
+
} : undefined);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
//# sourceMappingURL=tenant-write-lockdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/tenant-write-lockdown.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\n/**\n * The lockdown gate for VISITOR writes on a customer's live site\n * (AGL-1511) — form submissions, cart changes, checkout.\n *\n * Why this is its own chokepoint rather than the console's. The tenant\n * middleware, which is where AGL-1501 enforces, **does not match `/api`**\n * (see the matcher in apps/tenant/middleware.ts): it exists to stop CACHED\n * PAGES serving. So the site's own write endpoints have had no lockdown\n * enforcement at all — under a full org takedown the pages 503 while a form\n * POST still writes. This closes that for both modes.\n *\n * The product decision this file encodes, from the issue: a visitor-facing\n * write on a read-serving site gets a **polite inline pause, never a\n * page-level 503**. A customer's site staying up and earning is the entire\n * reason read-only mode exists instead of full lockdown, and answering their\n * visitors with an outage page would spend exactly what the mode was\n * protecting. So the refusal is a 423 with visitor copy the block renders\n * inline, and the page around it never stops rendering.\n *\n * Under a FULL lock the same 423 is returned rather than a 503, and that is\n * deliberate too: the middleware has already replaced the whole page for\n * anyone loading it, so a write arriving here is a stale tab or a script,\n * and a machine-readable refusal is more useful to both than an HTML page.\n *\n * NO staff bypass and NO user scope, matching /api/lockdown-verdict: this is\n * a public website, the caller is anonymous, and a visitor's identity plays\n * no part in whether a site accepts an order.\n */\n\nimport {\n isLockdownActive,\n lockdownBlocks,\n type LockdownIntent,\n lockdownIntentForMethod,\n type LockdownPausedSurface,\n lockdownPausedNotice,\n type LockdownState,\n normalizeHostLockdown,\n normalizeOrgLockdown,\n resolveLockdown,\n} from '@aglyn/aglyn/server'\nimport {\n getPlatformLockdown,\n heldSiteTakedown,\n lockdownJsonResponse,\n rememberSiteTakedown,\n} from './lockdown'\nimport { getHostDocAdmin, getOrgForHost } from './organizations'\n\n/**\n * The active platform/org/host lockdown for one site, or null.\n *\n * Both reads are already paid by the callers on this path — the dispatcher\n * reads the host doc for its plugin deny-list and the org doc for\n * enablement, and both getters are `React.cache`-deduped per request — so\n * the gate costs one TTL-cached platform read and nothing else.\n *\n * FAIL OPEN on any error, matching every other lockdown reader: an\n * unreachable Firestore is an outage, not a lockdown, and a shop that stops\n * taking orders because a read timed out is a worse failure than the one\n * being guarded against.\n *\n * …EXCEPT for a takedown (AGL-1881, extending AGL-1621). One class of lock\n * inverts that cost function — \"the database was down\" is not an answer to a\n * court order — and until now the ledger that carries it covered only the\n * four scopes whose lock lives in `lockdowns/*`. Org and host locks live on\n * the org/host document, so the two scopes MOST likely to be carrying a\n * legal order were the two that fell through. Every successful read now\n * records its org and host verdict, and a failed read serves back whichever\n * of them was a takedown and is still active.\n *\n * Note what is NOT done here: the catch does not fail closed on the state it\n * could not read. On a read failure `suspendedEnforcement` is exactly as\n * unreadable as everything else, which is the whole reason a separate\n * durable ledger exists — and a catch that locked unconditionally would take\n * every shop on the platform offline on any transient blip, which is the\n * deliberate posture this keeps for the default case.\n */\nexport async function getSiteLockdown(\n hostId: string,\n nowMs = Date.now(),\n): Promise<LockdownState | null> {\n if (!hostId) return null\n try {\n const [platform, resolved, host] = await Promise.all([\n getPlatformLockdown(),\n getOrgForHost(hostId),\n getHostDocAdmin(hostId),\n ])\n const org = normalizeOrgLockdown(resolved?.org as never)\n const hostState = normalizeHostLockdown(host as never)\n rememberSiteTakedown(hostId, { org, host: hostState })\n const state = resolveLockdown({ platform, org, host: hostState }, nowMs)\n return isLockdownActive(state, nowMs) ? state : null\n } catch (error) {\n console.error('[tenant-write-lockdown] verdict failed', hostId, error)\n const held = heldSiteTakedown(hostId, nowMs)\n // The shipped fail-open answer, returned by the same statement it always\n // was, for every case where nothing takedown-class was ever observed.\n if (!held.org && !held.host) return null\n // The platform read is TTL-cached and swallows its own errors (it carries\n // its own ledger entry), so asking again here costs nothing and keeps a\n // wider lock from being masked by a narrower held one.\n const platform = await getPlatformLockdown().catch(() => null)\n const state = resolveLockdown(\n { platform, org: held.org, host: held.host },\n nowMs,\n )\n return isLockdownActive(state, nowMs) ? state : null\n }\n}\n\n/**\n * The refusal a visitor-facing write route returns, or null to proceed:\n *\n * const paused = await visitorWriteRefusal({ hostId, request, surface })\n * if (paused) return paused\n *\n * `surface` picks the WORDS only (`lockdownPausedNotice`) — the checkout\n * sentence has to promise no card was charged, and no generic copy can.\n */\nexport async function visitorWriteRefusal(options: {\n hostId: string\n /** The request whose method decides read vs write. */\n request?: { method?: string } | null\n /** Overrides `request`; `write` when neither is given. */\n intent?: LockdownIntent\n surface: LockdownPausedSurface\n nowMs?: number\n}): Promise<Response | null> {\n const nowMs = options.nowMs ?? Date.now()\n const intent =\n options.intent ??\n (options.request ? lockdownIntentForMethod(options.request.method) : 'write')\n // This is a WRITE gate: reads leave before any Firestore read, so the hot\n // unauthenticated GET path pays nothing. A full lock's refusal of tenant\n // API *reads* is not attempted here and is unchanged from today — the\n // middleware owns the page surface, and a read-refusing API gate is its\n // own decision with its own blast radius (filed separately).\n if (intent === 'read') return null\n const state = await getSiteLockdown(options.hostId, nowMs)\n if (!lockdownBlocks(state, intent)) return null\n return lockdownJsonResponse(state as LockdownState, {\n notice: lockdownPausedNotice(options.surface),\n })\n}\n\n/**\n * The refusal a visitor-facing route that SERVES SITE CONTENT over `/api`\n * returns under a FULL lock, or null to proceed (AGL-2495):\n *\n * const down = await visitorContentRefusal({ hostId })\n * if (down) return down\n *\n * The gap this closes, found by the AGL-1621 drill. The tenant middleware\n * is what takes a locked site off the air, and its matcher deliberately\n * excludes `/api` — so a full org or host takedown 503s every page while an\n * API route that composes and returns screen nodes keeps handing the site's\n * content out to anyone who can name a screen. `visitorWriteRefusal` above\n * cannot answer that: it leaves before any read, by design, because it is a\n * WRITE gate on the hot unauthenticated GET path.\n *\n * FULL locks only, and that is the whole point of it being a separate\n * helper rather than a flag on the write gate. Read-only mode exists so a\n * customer's site keeps serving and earning; refusing reads there would\n * spend exactly what the mode protects. `lockdownBlocks(state, 'read')` is\n * true only when the mode is full, which is precisely the case where the\n * pages are already gone.\n *\n * Deliberately NOT applied to the whole tenant API surface. A read-refusing\n * gate on every route would take the lock notice, the verdict probe and the\n * abuse intake down with the site. This is for the routes that serve the\n * SITE ITSELF — a composed node tree — where continuing to serve is\n * continuing to publish. `serve-media-cdn.ts` made the same call for bytes\n * (AGL-1520); this is the node-tree half of it.\n *\n * Fails OPEN through `getSiteLockdown`, like every other reader here.\n */\nexport async function visitorContentRefusal(options: {\n hostId: string\n surface?: LockdownPausedSurface\n nowMs?: number\n}): Promise<Response | null> {\n const nowMs = options.nowMs ?? Date.now()\n const state = await getSiteLockdown(options.hostId, nowMs)\n if (!lockdownBlocks(state, 'read')) return null\n return lockdownJsonResponse(\n state as LockdownState,\n options.surface ? { notice: lockdownPausedNotice(options.surface) } : undefined,\n )\n}\n"],"names":["isLockdownActive","lockdownBlocks","lockdownIntentForMethod","lockdownPausedNotice","normalizeHostLockdown","normalizeOrgLockdown","resolveLockdown","getPlatformLockdown","heldSiteTakedown","lockdownJsonResponse","rememberSiteTakedown","getHostDocAdmin","getOrgForHost","getSiteLockdown","hostId","nowMs","Date","now","platform","resolved","host","Promise","all","org","hostState","state","error","console","held","catch","visitorWriteRefusal","options","intent","request","method","notice","surface","visitorContentRefusal","undefined"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GAED,SACEA,gBAAgB,EAChBC,cAAc,EAEdC,uBAAuB,EAEvBC,oBAAoB,EAEpBC,qBAAqB,EACrBC,oBAAoB,EACpBC,eAAe,QACV,sBAAqB;AAC5B,SACEC,mBAAmB,EACnBC,gBAAgB,EAChBC,oBAAoB,EACpBC,oBAAoB,QACf,gBAAY;AACnB,SAASC,eAAe,EAAEC,aAAa,QAAQ,qBAAiB;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,eAAeC,gBACpBC,MAAc,EACdC,QAAQC,KAAKC,GAAG,EAAE;IAElB,IAAI,CAACH,QAAQ,OAAO;IACpB,IAAI;QACF,MAAM,CAACI,UAAUC,UAAUC,KAAK,GAAG,MAAMC,QAAQC,GAAG,CAAC;YACnDf;YACAK,cAAcE;YACdH,gBAAgBG;SACjB;QACD,MAAMS,MAAMlB,qBAAqBc,4BAAAA,SAAUI,GAAG;QAC9C,MAAMC,YAAYpB,sBAAsBgB;QACxCV,qBAAqBI,QAAQ;YAAES;YAAKH,MAAMI;QAAU;QACpD,MAAMC,QAAQnB,gBAAgB;YAAEY;YAAUK;YAAKH,MAAMI;QAAU,GAAGT;QAClE,OAAOf,iBAAiByB,OAAOV,SAASU,QAAQ;IAClD,EAAE,OAAOC,OAAO;QACdC,QAAQD,KAAK,CAAC,0CAA0CZ,QAAQY;QAChE,MAAME,OAAOpB,iBAAiBM,QAAQC;QACtC,yEAAyE;QACzE,sEAAsE;QACtE,IAAI,CAACa,KAAKL,GAAG,IAAI,CAACK,KAAKR,IAAI,EAAE,OAAO;QACpC,0EAA0E;QAC1E,wEAAwE;QACxE,uDAAuD;QACvD,MAAMF,WAAW,MAAMX,sBAAsBsB,KAAK,CAAC,IAAM;QACzD,MAAMJ,QAAQnB,gBACZ;YAAEY;YAAUK,KAAKK,KAAKL,GAAG;YAAEH,MAAMQ,KAAKR,IAAI;QAAC,GAC3CL;QAEF,OAAOf,iBAAiByB,OAAOV,SAASU,QAAQ;IAClD;AACF;AAEA;;;;;;;;CAQC,GACD,OAAO,eAAeK,oBAAoBC,OAQzC;QACeA,gBAEZA;IAFF,MAAMhB,SAAQgB,iBAAAA,QAAQhB,KAAK,YAAbgB,iBAAiBf,KAAKC,GAAG;IACvC,MAAMe,UACJD,kBAAAA,QAAQC,MAAM,YAAdD,kBACCA,QAAQE,OAAO,GAAG/B,wBAAwB6B,QAAQE,OAAO,CAACC,MAAM,IAAI;IACvE,0EAA0E;IAC1E,yEAAyE;IACzE,sEAAsE;IACtE,wEAAwE;IACxE,6DAA6D;IAC7D,IAAIF,WAAW,QAAQ,OAAO;IAC9B,MAAMP,QAAQ,MAAMZ,gBAAgBkB,QAAQjB,MAAM,EAAEC;IACpD,IAAI,CAACd,eAAewB,OAAOO,SAAS,OAAO;IAC3C,OAAOvB,qBAAqBgB,OAAwB;QAClDU,QAAQhC,qBAAqB4B,QAAQK,OAAO;IAC9C;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BC,GACD,OAAO,eAAeC,sBAAsBN,OAI3C;QACeA;IAAd,MAAMhB,SAAQgB,iBAAAA,QAAQhB,KAAK,YAAbgB,iBAAiBf,KAAKC,GAAG;IACvC,MAAMQ,QAAQ,MAAMZ,gBAAgBkB,QAAQjB,MAAM,EAAEC;IACpD,IAAI,CAACd,eAAewB,OAAO,SAAS,OAAO;IAC3C,OAAOhB,qBACLgB,OACAM,QAAQK,OAAO,GAAG;QAAED,QAAQhC,qBAAqB4B,QAAQK,OAAO;IAAE,IAAIE;AAE1E"}
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
export interface FakeFirestore {
|
|
18
|
+
/**
|
|
19
|
+
* Every document in a collection, keyed by id.
|
|
20
|
+
*
|
|
21
|
+
* A SUBcollection is addressed by its full path — `hosts/host-1/suppressions`
|
|
22
|
+
* — which is also how it is seeded.
|
|
23
|
+
*/
|
|
24
|
+
docs: (collection: string) => Record<string, any>;
|
|
25
|
+
collection: (name: string) => any;
|
|
26
|
+
/**
|
|
27
|
+
* Several documents in one round trip, the shape the real `getAll` has:
|
|
28
|
+
* the results come back POSITIONALLY, one per reference, so a caller that
|
|
29
|
+
* zips them back against its own input list gets what it asked for.
|
|
30
|
+
*/
|
|
31
|
+
getAll: (...refs: any[]) => Promise<any[]>;
|
|
32
|
+
/**
|
|
33
|
+
* A transaction, real enough for a read-then-write body.
|
|
34
|
+
*
|
|
35
|
+
* Reads pass straight through and writes are held until the body resolves,
|
|
36
|
+
* which is the property the code under test depends on: a writer that
|
|
37
|
+
* decides what to store FROM what it read must not observe its own
|
|
38
|
+
* half-finished write. It does not model contention or retries — nothing in
|
|
39
|
+
* this repo's transaction bodies branches on a retry, and a fake that
|
|
40
|
+
* pretended to would be asserting the fake.
|
|
41
|
+
*/
|
|
42
|
+
runTransaction: <T>(body: (transaction: any) => Promise<T>) => Promise<T>;
|
|
43
|
+
}
|
|
44
|
+
export declare function fakeFirestore(seed?: Record<string, Record<string, any>>): FakeFirestore;
|
|
45
|
+
export default fakeFirestore;
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
*/ /**
|
|
18
|
+
* A Firestore stand-in for specs that span MORE THAN ONE COLLECTION
|
|
19
|
+
* (AGL-1592). Test-only; deliberately not exported from the barrel.
|
|
20
|
+
*
|
|
21
|
+
* The one-collection fakes elsewhere in this directory return the same
|
|
22
|
+
* document whatever collection name they are handed, which is fine when the
|
|
23
|
+
* code under test touches one document and actively misleading when it does
|
|
24
|
+
* not: the phone opt-out path reads `users/{uid}` and `contactSuppressions/{n}`
|
|
25
|
+
* and the whole point is that they are different records. A fake that conflated
|
|
26
|
+
* them would pass while the real thing wrote the marker on top of the
|
|
27
|
+
* suppression.
|
|
28
|
+
*
|
|
29
|
+
* `FieldValue.delete()` is honoured for real, because "the field is gone" and
|
|
30
|
+
* "the field holds a sentinel object" are different states and only the first
|
|
31
|
+
* one makes `seedUserProfile`'s blank check fire.
|
|
32
|
+
*/ import { FieldValue } from "firebase-admin/firestore";
|
|
33
|
+
const DELETE = FieldValue.delete();
|
|
34
|
+
export function fakeFirestore(seed = {}) {
|
|
35
|
+
const store = JSON.parse(JSON.stringify(seed));
|
|
36
|
+
const snapshotFor = (collection, id)=>{
|
|
37
|
+
var _store_collection;
|
|
38
|
+
const data = (_store_collection = store[collection]) == null ? void 0 : _store_collection[id];
|
|
39
|
+
return {
|
|
40
|
+
id,
|
|
41
|
+
exists: data !== undefined,
|
|
42
|
+
get: (field)=>data == null ? void 0 : data[field],
|
|
43
|
+
data: ()=>data
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const fake = {
|
|
47
|
+
docs: (collection)=>{
|
|
48
|
+
var _store_collection;
|
|
49
|
+
return (_store_collection = store[collection]) != null ? _store_collection : {};
|
|
50
|
+
},
|
|
51
|
+
getAll: async (...refs)=>refs.map((ref)=>snapshotFor(ref.collectionPath, ref.id)),
|
|
52
|
+
async runTransaction (body) {
|
|
53
|
+
const pending = [];
|
|
54
|
+
const transaction = {
|
|
55
|
+
get: (ref)=>ref.get(),
|
|
56
|
+
set: (ref, data, options)=>{
|
|
57
|
+
pending.push(()=>ref.set(data, options));
|
|
58
|
+
return transaction;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const result = await body(transaction);
|
|
62
|
+
for (const write of pending)await write();
|
|
63
|
+
return result;
|
|
64
|
+
},
|
|
65
|
+
collection (name) {
|
|
66
|
+
var _store_name;
|
|
67
|
+
store[name] = (_store_name = store[name]) != null ? _store_name : {};
|
|
68
|
+
const api = {
|
|
69
|
+
doc: (id)=>({
|
|
70
|
+
id,
|
|
71
|
+
/**
|
|
72
|
+
* Which collection this reference came from, so `getAll` can resolve
|
|
73
|
+
* a bare reference the way the real client does.
|
|
74
|
+
*/ collectionPath: name,
|
|
75
|
+
/**
|
|
76
|
+
* A subcollection is a collection at the joined path, so the fake
|
|
77
|
+
* stays one flat map. Nesting matters here because a per-site list
|
|
78
|
+
* lives under the site — a fake that returned the same documents for
|
|
79
|
+
* `hosts/a/suppressions` and `hosts/b/suppressions` would certify a
|
|
80
|
+
* sender that leaks one site's unsubscribes into another's.
|
|
81
|
+
*/ collection: (sub)=>fake.collection(`${name}/${id}/${sub}`),
|
|
82
|
+
get: async ()=>snapshotFor(name, id),
|
|
83
|
+
set: async (data, options)=>{
|
|
84
|
+
var _store_name_id;
|
|
85
|
+
const base = (options == null ? void 0 : options.merge) ? (_store_name_id = store[name][id]) != null ? _store_name_id : {} : {};
|
|
86
|
+
const next = _extends({}, base);
|
|
87
|
+
for (const [key, value] of Object.entries(data)){
|
|
88
|
+
// A real delete, not a stored sentinel — see the header.
|
|
89
|
+
if (value && typeof value === 'object' && DELETE.isEqual(value)) {
|
|
90
|
+
delete next[key];
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Server timestamps arrive as sentinels too. Freeze them into
|
|
94
|
+
// something a spec can assert on and a date can be read from.
|
|
95
|
+
next[key] = value && typeof value === 'object' && typeof value.isEqual === 'function' ? {
|
|
96
|
+
seconds: Math.floor(Date.now() / 1000)
|
|
97
|
+
} : value;
|
|
98
|
+
}
|
|
99
|
+
store[name][id] = next;
|
|
100
|
+
}
|
|
101
|
+
}),
|
|
102
|
+
// Ordering and limits are not what these specs are about; the queue
|
|
103
|
+
// read just needs to return everything it stored.
|
|
104
|
+
orderBy: ()=>api,
|
|
105
|
+
limit: ()=>api,
|
|
106
|
+
get: async ()=>({
|
|
107
|
+
docs: Object.keys(store[name]).map((id)=>snapshotFor(name, id))
|
|
108
|
+
}),
|
|
109
|
+
add: async (data)=>{
|
|
110
|
+
const id = `auto-${Object.keys(store[name]).length + 1}`;
|
|
111
|
+
store[name][id] = data;
|
|
112
|
+
return {
|
|
113
|
+
id
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
return api;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
return fake;
|
|
121
|
+
}
|
|
122
|
+
export default fakeFirestore;
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=test-firestore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/test-firestore.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\n/**\n * A Firestore stand-in for specs that span MORE THAN ONE COLLECTION\n * (AGL-1592). Test-only; deliberately not exported from the barrel.\n *\n * The one-collection fakes elsewhere in this directory return the same\n * document whatever collection name they are handed, which is fine when the\n * code under test touches one document and actively misleading when it does\n * not: the phone opt-out path reads `users/{uid}` and `contactSuppressions/{n}`\n * and the whole point is that they are different records. A fake that conflated\n * them would pass while the real thing wrote the marker on top of the\n * suppression.\n *\n * `FieldValue.delete()` is honoured for real, because \"the field is gone\" and\n * \"the field holds a sentinel object\" are different states and only the first\n * one makes `seedUserProfile`'s blank check fire.\n */\n\nimport { FieldValue } from 'firebase-admin/firestore'\n\nconst DELETE = FieldValue.delete()\n\nexport interface FakeFirestore {\n /**\n * Every document in a collection, keyed by id.\n *\n * A SUBcollection is addressed by its full path — `hosts/host-1/suppressions`\n * — which is also how it is seeded.\n */\n docs: (collection: string) => Record<string, any>\n collection: (name: string) => any\n /**\n * Several documents in one round trip, the shape the real `getAll` has:\n * the results come back POSITIONALLY, one per reference, so a caller that\n * zips them back against its own input list gets what it asked for.\n */\n getAll: (...refs: any[]) => Promise<any[]>\n /**\n * A transaction, real enough for a read-then-write body.\n *\n * Reads pass straight through and writes are held until the body resolves,\n * which is the property the code under test depends on: a writer that\n * decides what to store FROM what it read must not observe its own\n * half-finished write. It does not model contention or retries — nothing in\n * this repo's transaction bodies branches on a retry, and a fake that\n * pretended to would be asserting the fake.\n */\n runTransaction: <T>(body: (transaction: any) => Promise<T>) => Promise<T>\n}\n\nexport function fakeFirestore(\n seed: Record<string, Record<string, any>> = {},\n): FakeFirestore {\n const store: Record<string, Record<string, any>> = JSON.parse(\n JSON.stringify(seed),\n )\n\n const snapshotFor = (collection: string, id: string) => {\n const data = store[collection]?.[id]\n return {\n id,\n exists: data !== undefined,\n get: (field: string) => data?.[field],\n data: () => data,\n }\n }\n\n const fake: FakeFirestore = {\n docs: (collection: string) => store[collection] ?? {},\n getAll: async (...refs: any[]) =>\n refs.map((ref) => snapshotFor(ref.collectionPath, ref.id)),\n async runTransaction<T>(body: (transaction: any) => Promise<T>): Promise<T> {\n const pending: Array<() => Promise<void>> = []\n const transaction = {\n get: (ref: any) => ref.get(),\n set: (ref: any, data: Record<string, any>, options?: { merge?: boolean }) => {\n pending.push(() => ref.set(data, options))\n return transaction\n },\n }\n const result = await body(transaction)\n for (const write of pending) await write()\n return result\n },\n collection(name: string) {\n store[name] = store[name] ?? {}\n const api: any = {\n doc: (id: string) => ({\n id,\n /**\n * Which collection this reference came from, so `getAll` can resolve\n * a bare reference the way the real client does.\n */\n collectionPath: name,\n /**\n * A subcollection is a collection at the joined path, so the fake\n * stays one flat map. Nesting matters here because a per-site list\n * lives under the site — a fake that returned the same documents for\n * `hosts/a/suppressions` and `hosts/b/suppressions` would certify a\n * sender that leaks one site's unsubscribes into another's.\n */\n collection: (sub: string) => fake.collection(`${name}/${id}/${sub}`),\n get: async () => snapshotFor(name, id),\n set: async (data: Record<string, any>, options?: { merge?: boolean }) => {\n const base = options?.merge ? (store[name][id] ?? {}) : {}\n const next: Record<string, any> = { ...base }\n for (const [key, value] of Object.entries(data)) {\n // A real delete, not a stored sentinel — see the header.\n if (value && typeof value === 'object' && DELETE.isEqual(value as any)) {\n delete next[key]\n continue\n }\n // Server timestamps arrive as sentinels too. Freeze them into\n // something a spec can assert on and a date can be read from.\n next[key] =\n value && typeof value === 'object' && typeof (value as any).isEqual === 'function'\n ? { seconds: Math.floor(Date.now() / 1000) }\n : value\n }\n store[name][id] = next\n },\n }),\n // Ordering and limits are not what these specs are about; the queue\n // read just needs to return everything it stored.\n orderBy: () => api,\n limit: () => api,\n get: async () => ({\n docs: Object.keys(store[name]).map((id) => snapshotFor(name, id)),\n }),\n add: async (data: Record<string, any>) => {\n const id = `auto-${Object.keys(store[name]).length + 1}`\n store[name][id] = data\n return { id }\n },\n }\n return api\n },\n }\n return fake\n}\n\nexport default fakeFirestore\n"],"names":["FieldValue","DELETE","delete","fakeFirestore","seed","store","JSON","parse","stringify","snapshotFor","collection","id","data","exists","undefined","get","field","fake","docs","getAll","refs","map","ref","collectionPath","runTransaction","body","pending","transaction","set","options","push","result","write","name","api","doc","sub","base","merge","next","key","value","Object","entries","isEqual","seconds","Math","floor","Date","now","orderBy","limit","keys","add","length"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;CAeC,GAED,SAASA,UAAU,QAAQ,2BAA0B;AAErD,MAAMC,SAASD,WAAWE,MAAM;AA8BhC,OAAO,SAASC,cACdC,OAA4C,CAAC,CAAC;IAE9C,MAAMC,QAA6CC,KAAKC,KAAK,CAC3DD,KAAKE,SAAS,CAACJ;IAGjB,MAAMK,cAAc,CAACC,YAAoBC;YAC1BN;QAAb,MAAMO,QAAOP,oBAAAA,KAAK,CAACK,WAAW,qBAAjBL,iBAAmB,CAACM,GAAG;QACpC,OAAO;YACLA;YACAE,QAAQD,SAASE;YACjBC,KAAK,CAACC,QAAkBJ,wBAAAA,IAAM,CAACI,MAAM;YACrCJ,MAAM,IAAMA;QACd;IACF;IAEA,MAAMK,OAAsB;QAC1BC,MAAM,CAACR;gBAAuBL;oBAAAA,oBAAAA,KAAK,CAACK,WAAW,YAAjBL,oBAAqB,CAAC;;QACpDc,QAAQ,OAAO,GAAGC,OAChBA,KAAKC,GAAG,CAAC,CAACC,MAAQb,YAAYa,IAAIC,cAAc,EAAED,IAAIX,EAAE;QAC1D,MAAMa,gBAAkBC,IAAsC;YAC5D,MAAMC,UAAsC,EAAE;YAC9C,MAAMC,cAAc;gBAClBZ,KAAK,CAACO,MAAaA,IAAIP,GAAG;gBAC1Ba,KAAK,CAACN,KAAUV,MAA2BiB;oBACzCH,QAAQI,IAAI,CAAC,IAAMR,IAAIM,GAAG,CAAChB,MAAMiB;oBACjC,OAAOF;gBACT;YACF;YACA,MAAMI,SAAS,MAAMN,KAAKE;YAC1B,KAAK,MAAMK,SAASN,QAAS,MAAMM;YACnC,OAAOD;QACT;QACArB,YAAWuB,IAAY;gBACP5B;YAAdA,KAAK,CAAC4B,KAAK,IAAG5B,cAAAA,KAAK,CAAC4B,KAAK,YAAX5B,cAAe,CAAC;YAC9B,MAAM6B,MAAW;gBACfC,KAAK,CAACxB,KAAgB,CAAA;wBACpBA;wBACA;;;WAGC,GACDY,gBAAgBU;wBAChB;;;;;;WAMC,GACDvB,YAAY,CAAC0B,MAAgBnB,KAAKP,UAAU,CAAC,GAAGuB,KAAK,CAAC,EAAEtB,GAAG,CAAC,EAAEyB,KAAK;wBACnErB,KAAK,UAAYN,YAAYwB,MAAMtB;wBACnCiB,KAAK,OAAOhB,MAA2BiB;gCACNxB;4BAA/B,MAAMgC,OAAOR,CAAAA,2BAAAA,QAASS,KAAK,KAAIjC,iBAAAA,KAAK,CAAC4B,KAAK,CAACtB,GAAG,YAAfN,iBAAmB,CAAC,IAAK,CAAC;4BACzD,MAAMkC,OAA4B,aAAKF;4BACvC,KAAK,MAAM,CAACG,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAAC/B,MAAO;gCAC/C,yDAAyD;gCACzD,IAAI6B,SAAS,OAAOA,UAAU,YAAYxC,OAAO2C,OAAO,CAACH,QAAe;oCACtE,OAAOF,IAAI,CAACC,IAAI;oCAChB;gCACF;gCACA,8DAA8D;gCAC9D,8DAA8D;gCAC9DD,IAAI,CAACC,IAAI,GACPC,SAAS,OAAOA,UAAU,YAAY,OAAO,AAACA,MAAcG,OAAO,KAAK,aACpE;oCAAEC,SAASC,KAAKC,KAAK,CAACC,KAAKC,GAAG,KAAK;gCAAM,IACzCR;4BACR;4BACApC,KAAK,CAAC4B,KAAK,CAACtB,GAAG,GAAG4B;wBACpB;oBACF,CAAA;gBACA,oEAAoE;gBACpE,kDAAkD;gBAClDW,SAAS,IAAMhB;gBACfiB,OAAO,IAAMjB;gBACbnB,KAAK,UAAa,CAAA;wBAChBG,MAAMwB,OAAOU,IAAI,CAAC/C,KAAK,CAAC4B,KAAK,EAAEZ,GAAG,CAAC,CAACV,KAAOF,YAAYwB,MAAMtB;oBAC/D,CAAA;gBACA0C,KAAK,OAAOzC;oBACV,MAAMD,KAAK,CAAC,KAAK,EAAE+B,OAAOU,IAAI,CAAC/C,KAAK,CAAC4B,KAAK,EAAEqB,MAAM,GAAG,GAAG;oBACxDjD,KAAK,CAAC4B,KAAK,CAACtB,GAAG,GAAGC;oBAClB,OAAO;wBAAED;oBAAG;gBACd;YACF;YACA,OAAOuB;QACT;IACF;IACA,OAAOjB;AACT;AAEA,eAAed,cAAa"}
|