@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,184 @@
|
|
|
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
|
+
* One DNS lookup, pinned to public resolvers, that can say "I do not know".
|
|
18
|
+
*
|
|
19
|
+
* The same shape appeared independently in `/api/domains/verify` (AGL-734) and
|
|
20
|
+
* in `sso-provisioning.ts` (AGL-1210), and a third copy was about to be
|
|
21
|
+
* written for sending domains. It is here once instead.
|
|
22
|
+
*
|
|
23
|
+
* ## Why the resolvers are pinned
|
|
24
|
+
*
|
|
25
|
+
* The runtime's default resolver is not a neutral observer. A stale zone once
|
|
26
|
+
* made the hosting platform's own resolver return NXDOMAIN for records every
|
|
27
|
+
* public resolver could see, which reads as "the customer deleted their
|
|
28
|
+
* record" and is indistinguishable from the real thing. Asking a public
|
|
29
|
+
* resolver first, and falling back to the runtime's only when the pinned ones
|
|
30
|
+
* are unreachable, keeps a genuine "no such record" answer conclusive.
|
|
31
|
+
*
|
|
32
|
+
* ## Why there are three outcomes and not two
|
|
33
|
+
*
|
|
34
|
+
* A boolean cannot tell "the record is gone" from "nobody answered", and the
|
|
35
|
+
* difference decides whether a customer's working configuration gets marked
|
|
36
|
+
* broken during someone else's outage. `NXDOMAIN`/`ENOTFOUND`/`ENODATA` are
|
|
37
|
+
* ANSWERS — that name has no such record — and stay conclusive. Anything else,
|
|
38
|
+
* from both the pinned resolvers and the fallback, is `unreachable`, and a
|
|
39
|
+
* caller must treat it as evidence of nothing in either direction.
|
|
40
|
+
*/ import { promises as dns, Resolver as CallbackResolver } from "dns";
|
|
41
|
+
/** Resolvers asked before the runtime's own. */ export const PUBLIC_DNS_RESOLVERS = [
|
|
42
|
+
'1.1.1.1',
|
|
43
|
+
'8.8.8.8'
|
|
44
|
+
];
|
|
45
|
+
/** Whether a resolver error code is an answer rather than a failure to get one. */ export function isConclusiveDnsCode(code) {
|
|
46
|
+
return code === 'ENOTFOUND' || code === 'ENODATA' || code === 'NXDOMAIN';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* TXT records at `host`, each joined from its chunks and trimmed.
|
|
50
|
+
*
|
|
51
|
+
* A TXT answer arrives as an array of string CHUNKS per record, because DNS
|
|
52
|
+
* splits strings longer than 255 bytes. Comparing without joining means a long
|
|
53
|
+
* value — every DKIM public key, for instance — can never match.
|
|
54
|
+
*/ export async function lookupTxt(host) {
|
|
55
|
+
const flatten = (records)=>records.map((chunks)=>chunks.join('').trim());
|
|
56
|
+
try {
|
|
57
|
+
const resolver = new CallbackResolver();
|
|
58
|
+
resolver.setServers(PUBLIC_DNS_RESOLVERS);
|
|
59
|
+
const records = await new Promise((resolve, reject)=>{
|
|
60
|
+
resolver.resolveTxt(host, (error, addresses)=>error ? reject(error) : resolve(addresses));
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
answered: true,
|
|
64
|
+
records: flatten(records)
|
|
65
|
+
};
|
|
66
|
+
} catch (error) {
|
|
67
|
+
const code = error == null ? void 0 : error.code;
|
|
68
|
+
if (isConclusiveDnsCode(code)) return {
|
|
69
|
+
answered: true,
|
|
70
|
+
records: []
|
|
71
|
+
};
|
|
72
|
+
try {
|
|
73
|
+
return {
|
|
74
|
+
answered: true,
|
|
75
|
+
records: flatten(await dns.resolveTxt(host))
|
|
76
|
+
};
|
|
77
|
+
} catch (fallbackError) {
|
|
78
|
+
const fallbackCode = fallbackError == null ? void 0 : fallbackError.code;
|
|
79
|
+
if (isConclusiveDnsCode(fallbackCode)) return {
|
|
80
|
+
answered: true,
|
|
81
|
+
records: []
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
answered: false,
|
|
85
|
+
records: []
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* CAA records at `host`, as `<flags> <tag> "<value>"` strings.
|
|
92
|
+
*
|
|
93
|
+
* NOT walked up the tree. The caller asks about one exact name, because the
|
|
94
|
+
* question this exists for is "does THIS name publish a policy of its own",
|
|
95
|
+
* and a walk would answer a different one — a parent's policy is inherited by
|
|
96
|
+
* resolution, not by publication, and reporting the parent's records as the
|
|
97
|
+
* child's would say a name restricts issuance when it publishes nothing.
|
|
98
|
+
*/ export async function lookupCaa(host) {
|
|
99
|
+
const normalize = (records)=>records.map((entry)=>{
|
|
100
|
+
var _ref, _ref1, _row_issue;
|
|
101
|
+
const row = entry;
|
|
102
|
+
const tag = (row == null ? void 0 : row.issue) !== undefined ? 'issue' : (row == null ? void 0 : row.issuewild) !== undefined ? 'issuewild' : (row == null ? void 0 : row.iodef) !== undefined ? 'iodef' : '';
|
|
103
|
+
if (!tag) return '';
|
|
104
|
+
const value = String((_ref = (_ref1 = (_row_issue = row.issue) != null ? _row_issue : row.issuewild) != null ? _ref1 : row.iodef) != null ? _ref : '').trim();
|
|
105
|
+
return `${Number(row == null ? void 0 : row.critical) || 0} ${tag} "${value}"`;
|
|
106
|
+
}).filter(Boolean);
|
|
107
|
+
try {
|
|
108
|
+
const resolver = new CallbackResolver();
|
|
109
|
+
resolver.setServers(PUBLIC_DNS_RESOLVERS);
|
|
110
|
+
const records = await new Promise((resolve, reject)=>{
|
|
111
|
+
resolver.resolveCaa(host, (error, addresses)=>error ? reject(error) : resolve(addresses));
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
answered: true,
|
|
115
|
+
records: normalize(records)
|
|
116
|
+
};
|
|
117
|
+
} catch (error) {
|
|
118
|
+
const code = error == null ? void 0 : error.code;
|
|
119
|
+
if (isConclusiveDnsCode(code)) return {
|
|
120
|
+
answered: true,
|
|
121
|
+
records: []
|
|
122
|
+
};
|
|
123
|
+
try {
|
|
124
|
+
return {
|
|
125
|
+
answered: true,
|
|
126
|
+
records: normalize(await dns.resolveCaa(host))
|
|
127
|
+
};
|
|
128
|
+
} catch (fallbackError) {
|
|
129
|
+
const fallbackCode = fallbackError == null ? void 0 : fallbackError.code;
|
|
130
|
+
if (isConclusiveDnsCode(fallbackCode)) return {
|
|
131
|
+
answered: true,
|
|
132
|
+
records: []
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
answered: false,
|
|
136
|
+
records: []
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** MX records at `host`, exchanges lowercased and stripped of a trailing dot. */ export async function lookupMx(host) {
|
|
142
|
+
const normalize = (records)=>records.map((entry)=>{
|
|
143
|
+
var _ref;
|
|
144
|
+
return {
|
|
145
|
+
exchange: String((_ref = entry == null ? void 0 : entry.exchange) != null ? _ref : '').trim().toLowerCase().replace(/\.$/, ''),
|
|
146
|
+
priority: Number(entry == null ? void 0 : entry.priority) || 0
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
try {
|
|
150
|
+
const resolver = new CallbackResolver();
|
|
151
|
+
resolver.setServers(PUBLIC_DNS_RESOLVERS);
|
|
152
|
+
const records = await new Promise((resolve, reject)=>{
|
|
153
|
+
resolver.resolveMx(host, (error, addresses)=>error ? reject(error) : resolve(addresses));
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
answered: true,
|
|
157
|
+
records: normalize(records)
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const code = error == null ? void 0 : error.code;
|
|
161
|
+
if (isConclusiveDnsCode(code)) return {
|
|
162
|
+
answered: true,
|
|
163
|
+
records: []
|
|
164
|
+
};
|
|
165
|
+
try {
|
|
166
|
+
return {
|
|
167
|
+
answered: true,
|
|
168
|
+
records: normalize(await dns.resolveMx(host))
|
|
169
|
+
};
|
|
170
|
+
} catch (fallbackError) {
|
|
171
|
+
const fallbackCode = fallbackError == null ? void 0 : fallbackError.code;
|
|
172
|
+
if (isConclusiveDnsCode(fallbackCode)) return {
|
|
173
|
+
answered: true,
|
|
174
|
+
records: []
|
|
175
|
+
};
|
|
176
|
+
return {
|
|
177
|
+
answered: false,
|
|
178
|
+
records: []
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=dns-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/dns-probe.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 * One DNS lookup, pinned to public resolvers, that can say \"I do not know\".\n *\n * The same shape appeared independently in `/api/domains/verify` (AGL-734) and\n * in `sso-provisioning.ts` (AGL-1210), and a third copy was about to be\n * written for sending domains. It is here once instead.\n *\n * ## Why the resolvers are pinned\n *\n * The runtime's default resolver is not a neutral observer. A stale zone once\n * made the hosting platform's own resolver return NXDOMAIN for records every\n * public resolver could see, which reads as \"the customer deleted their\n * record\" and is indistinguishable from the real thing. Asking a public\n * resolver first, and falling back to the runtime's only when the pinned ones\n * are unreachable, keeps a genuine \"no such record\" answer conclusive.\n *\n * ## Why there are three outcomes and not two\n *\n * A boolean cannot tell \"the record is gone\" from \"nobody answered\", and the\n * difference decides whether a customer's working configuration gets marked\n * broken during someone else's outage. `NXDOMAIN`/`ENOTFOUND`/`ENODATA` are\n * ANSWERS — that name has no such record — and stay conclusive. Anything else,\n * from both the pinned resolvers and the fallback, is `unreachable`, and a\n * caller must treat it as evidence of nothing in either direction.\n */\n\nimport { promises as dns, Resolver as CallbackResolver } from 'dns'\n\n/** Resolvers asked before the runtime's own. */\nexport const PUBLIC_DNS_RESOLVERS = ['1.1.1.1', '8.8.8.8']\n\n/** Whether a resolver error code is an answer rather than a failure to get one. */\nexport function isConclusiveDnsCode(code: string | undefined): boolean {\n return code === 'ENOTFOUND' || code === 'ENODATA' || code === 'NXDOMAIN'\n}\n\nexport interface DnsLookupResult<T> {\n /**\n * True when the lookup got an answer, including the answer \"there is\n * nothing here\". False only when nobody answered.\n */\n answered: boolean\n /** Empty on a conclusive miss and on an unreachable lookup alike. */\n records: T[]\n}\n\n/**\n * TXT records at `host`, each joined from its chunks and trimmed.\n *\n * A TXT answer arrives as an array of string CHUNKS per record, because DNS\n * splits strings longer than 255 bytes. Comparing without joining means a long\n * value — every DKIM public key, for instance — can never match.\n */\nexport async function lookupTxt(host: string): Promise<DnsLookupResult<string>> {\n const flatten = (records: string[][]) =>\n records.map((chunks) => chunks.join('').trim())\n\n try {\n const resolver = new CallbackResolver()\n resolver.setServers(PUBLIC_DNS_RESOLVERS)\n const records = await new Promise<string[][]>((resolve, reject) => {\n resolver.resolveTxt(host, (error, addresses) =>\n error ? reject(error) : resolve(addresses),\n )\n })\n return { answered: true, records: flatten(records) }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code\n if (isConclusiveDnsCode(code)) return { answered: true, records: [] }\n try {\n return { answered: true, records: flatten(await dns.resolveTxt(host)) }\n } catch (fallbackError) {\n const fallbackCode = (fallbackError as NodeJS.ErrnoException)?.code\n if (isConclusiveDnsCode(fallbackCode)) return { answered: true, records: [] }\n return { answered: false, records: [] }\n }\n }\n}\n\n/**\n * CAA records at `host`, as `<flags> <tag> \"<value>\"` strings.\n *\n * NOT walked up the tree. The caller asks about one exact name, because the\n * question this exists for is \"does THIS name publish a policy of its own\",\n * and a walk would answer a different one — a parent's policy is inherited by\n * resolution, not by publication, and reporting the parent's records as the\n * child's would say a name restricts issuance when it publishes nothing.\n */\nexport async function lookupCaa(host: string): Promise<DnsLookupResult<string>> {\n const normalize = (records: unknown[]) =>\n records\n .map((entry) => {\n const row = entry as { critical?: number; issue?: string; issuewild?: string; iodef?: string }\n const tag = row?.issue !== undefined\n ? 'issue'\n : row?.issuewild !== undefined\n ? 'issuewild'\n : row?.iodef !== undefined\n ? 'iodef'\n : ''\n if (!tag) return ''\n const value = String(\n row.issue ?? row.issuewild ?? row.iodef ?? '',\n ).trim()\n return `${Number(row?.critical) || 0} ${tag} \"${value}\"`\n })\n .filter(Boolean)\n\n try {\n const resolver = new CallbackResolver()\n resolver.setServers(PUBLIC_DNS_RESOLVERS)\n const records = await new Promise<unknown[]>((resolve, reject) => {\n resolver.resolveCaa(host, (error, addresses) =>\n error ? reject(error) : resolve(addresses as unknown[]),\n )\n })\n return { answered: true, records: normalize(records) }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code\n if (isConclusiveDnsCode(code)) return { answered: true, records: [] }\n try {\n return {\n answered: true,\n records: normalize((await dns.resolveCaa(host)) as unknown[]),\n }\n } catch (fallbackError) {\n const fallbackCode = (fallbackError as NodeJS.ErrnoException)?.code\n if (isConclusiveDnsCode(fallbackCode)) return { answered: true, records: [] }\n return { answered: false, records: [] }\n }\n }\n}\n\nexport interface MxRecord {\n exchange: string\n priority: number\n}\n\n/** MX records at `host`, exchanges lowercased and stripped of a trailing dot. */\nexport async function lookupMx(host: string): Promise<DnsLookupResult<MxRecord>> {\n const normalize = (records: { exchange: string; priority: number }[]) =>\n records.map((entry) => ({\n exchange: String(entry?.exchange ?? '')\n .trim()\n .toLowerCase()\n .replace(/\\.$/, ''),\n priority: Number(entry?.priority) || 0,\n }))\n\n try {\n const resolver = new CallbackResolver()\n resolver.setServers(PUBLIC_DNS_RESOLVERS)\n const records = await new Promise<{ exchange: string; priority: number }[]>(\n (resolve, reject) => {\n resolver.resolveMx(host, (error, addresses) =>\n error ? reject(error) : resolve(addresses),\n )\n },\n )\n return { answered: true, records: normalize(records) }\n } catch (error) {\n const code = (error as NodeJS.ErrnoException)?.code\n if (isConclusiveDnsCode(code)) return { answered: true, records: [] }\n try {\n return { answered: true, records: normalize(await dns.resolveMx(host)) }\n } catch (fallbackError) {\n const fallbackCode = (fallbackError as NodeJS.ErrnoException)?.code\n if (isConclusiveDnsCode(fallbackCode)) return { answered: true, records: [] }\n return { answered: false, records: [] }\n }\n }\n}\n"],"names":["promises","dns","Resolver","CallbackResolver","PUBLIC_DNS_RESOLVERS","isConclusiveDnsCode","code","lookupTxt","host","flatten","records","map","chunks","join","trim","resolver","setServers","Promise","resolve","reject","resolveTxt","error","addresses","answered","fallbackError","fallbackCode","lookupCaa","normalize","entry","row","tag","issue","undefined","issuewild","iodef","value","String","Number","critical","filter","Boolean","resolveCaa","lookupMx","exchange","toLowerCase","replace","priority","resolveMx"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
/** Firestore's document id ceiling, which the service counts in BYTES. */
|
|
18
|
+
export declare const MAX_DOCUMENT_ID_BYTES = 1500;
|
|
19
|
+
/**
|
|
20
|
+
* Whether `value` is usable as ONE opaque Firestore document id.
|
|
21
|
+
*
|
|
22
|
+
* Every clause is a path escape rather than a style preference: `/` is both
|
|
23
|
+
* the nesting hazard and the synchronous `.doc()` throw; `.` and `..` are
|
|
24
|
+
* traversal that Firestore rejects outright; the reserved form throws instead
|
|
25
|
+
* of reporting absence; and the length is Firestore's own limit, measured in
|
|
26
|
+
* UTF-8 bytes because that is what the limit counts — 376 emoji are 1504
|
|
27
|
+
* bytes and would sail through a `.length` check.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isDocumentId(value: unknown): value is string;
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
* `isDocumentId(value)` — may this value be handed to `.doc()` as ONE opaque
|
|
18
|
+
* document id (AGL-1771)?
|
|
19
|
+
*
|
|
20
|
+
* WHY THE QUESTION EXISTS. `CollectionReference.doc()` appends a
|
|
21
|
+
* SLASH-SEPARATED path and refuses it only when the resulting component count
|
|
22
|
+
* comes out odd (`@google-cloud/firestore`, `collection-reference.js`,
|
|
23
|
+
* measured in `842e72576`). It never treats its argument as one opaque id. So
|
|
24
|
+
* an unvalidated value taken from a request, a webhook tag, a cookie or a
|
|
25
|
+
* stored field gives three distinct failures, none of which is "not found":
|
|
26
|
+
*
|
|
27
|
+
* * an ODD component count (`a/b/c`) is a perfectly legal path, so the read
|
|
28
|
+
* or write lands beneath a document that does not exist — invisible to
|
|
29
|
+
* every console list, because they resolve the parent first;
|
|
30
|
+
* * an EVEN one (`half/path`) throws SYNCHRONOUSLY at the line building the
|
|
31
|
+
* ref, which is a different failure from a rejected promise and escapes
|
|
32
|
+
* any `.catch()` on one. Whether that becomes a clean 500 or rejects out
|
|
33
|
+
* of the handler depends entirely on whether the ref is built inside or
|
|
34
|
+
* outside the handler's `try`;
|
|
35
|
+
* * a reserved `__…__` id answers `INVALID_ARGUMENT` rather than an absent
|
|
36
|
+
* snapshot — the trap `542b1023f` hit with `products/__missing__`.
|
|
37
|
+
*
|
|
38
|
+
* WHY IT LIVES HERE. This is the third and fourth call site. AGL-1769 wrote
|
|
39
|
+
* the rule as `isCartId` in `libs/plugins/commerce/src/lib/server/cart-cookie.ts`
|
|
40
|
+
* and AGL-1768 copied it as a local `isDocumentId` in
|
|
41
|
+
* `libs/plugins/marketing/src/lib/server/email-events.ts`; AGL-1771's sweep
|
|
42
|
+
* then found two more handlers that need it. Duplication was the cheaper wait
|
|
43
|
+
* at two copies and stops being cheaper at four.
|
|
44
|
+
*
|
|
45
|
+
* A CORRECTION, since the second copy was made for a stated reason that turns
|
|
46
|
+
* out to be false: `@nx/enforce-module-boundaries` does NOT refuse an edge
|
|
47
|
+
* between two feature plugins. Every plugin carries only `aglyn:addons`, and
|
|
48
|
+
* that tag's rule lists `aglyn:addons` among its permitted targets — indeed
|
|
49
|
+
* `campaign-send.ts` already imports `@aglyn/plugins-commerce/model` today. The
|
|
50
|
+
* real objection to marketing importing commerce's cart-cookie module is not
|
|
51
|
+
* lint but sense: the predicate is not about carts, and reaching it through
|
|
52
|
+
* `@aglyn/plugins-commerce/server` would pull the whole commerce server graph
|
|
53
|
+
* — Stripe, the billing webhook — into the marketing plugin for six lines.
|
|
54
|
+
*
|
|
55
|
+
* `tenant-data-admin` is the right home instead because it is where Firestore
|
|
56
|
+
* paths are BUILT, and because {@link updateExisting} — the sibling primitive
|
|
57
|
+
* answering the same class of question, "must this document already exist?" —
|
|
58
|
+
* already lives beside it. Both plugins already depend on this library, so no
|
|
59
|
+
* new edge is created, and this module imports nothing, so none is created
|
|
60
|
+
* downstream either.
|
|
61
|
+
*
|
|
62
|
+
* IMPORT IT FROM THIS ENTRY POINT — `@aglyn/tenant-data-admin/server/document-id`
|
|
63
|
+
* — and not from the library barrel, which is exported there only for
|
|
64
|
+
* discoverability beside its siblings. Two reasons, both load-bearing:
|
|
65
|
+
*
|
|
66
|
+
* * this module imports NOTHING, and reaching it through `tenancy.ts` would
|
|
67
|
+
* pull ~35 server modules (media CDN, erasure, Next render-cache) into
|
|
68
|
+
* callers that today have no such graph. `cart-cookie.ts` imports only
|
|
69
|
+
* `crypto`; it should stay that way;
|
|
70
|
+
* * nearly every spec touching those callers already does
|
|
71
|
+
* `jest.mock('@aglyn/tenant-data-admin', …)` precisely because that graph
|
|
72
|
+
* will not load under jest. A barrel import would resolve to whatever the
|
|
73
|
+
* mock object happens to contain — `undefined` at best, and at worst a
|
|
74
|
+
* permissive stub that turns every path-shaped test value into a false
|
|
75
|
+
* green. A leaf entry point is not intercepted, so the predicate under test
|
|
76
|
+
* is always the real one.
|
|
77
|
+
*
|
|
78
|
+
* WHAT THIS IS NOT. Not an authorization check and not a claim that the
|
|
79
|
+
* document exists. It bounds a value to one path COMPONENT; whether that
|
|
80
|
+
* component names anything, and whether this caller may touch it, are separate
|
|
81
|
+
* questions that only the read, the write, or a gate can answer.
|
|
82
|
+
*/ /**
|
|
83
|
+
* Firestore reserves ids matching `__…__` and rejects them with
|
|
84
|
+
* `INVALID_ARGUMENT` rather than returning an absent snapshot.
|
|
85
|
+
*/ const RESERVED_DOCUMENT_ID = /^__.*__$/;
|
|
86
|
+
/** Firestore's document id ceiling, which the service counts in BYTES. */ export const MAX_DOCUMENT_ID_BYTES = 1500;
|
|
87
|
+
/**
|
|
88
|
+
* Whether `value` is usable as ONE opaque Firestore document id.
|
|
89
|
+
*
|
|
90
|
+
* Every clause is a path escape rather than a style preference: `/` is both
|
|
91
|
+
* the nesting hazard and the synchronous `.doc()` throw; `.` and `..` are
|
|
92
|
+
* traversal that Firestore rejects outright; the reserved form throws instead
|
|
93
|
+
* of reporting absence; and the length is Firestore's own limit, measured in
|
|
94
|
+
* UTF-8 bytes because that is what the limit counts — 376 emoji are 1504
|
|
95
|
+
* bytes and would sail through a `.length` check.
|
|
96
|
+
*/ export function isDocumentId(value) {
|
|
97
|
+
return typeof value === 'string' && value.length > 0 && !value.includes('/') && value !== '.' && value !== '..' && !RESERVED_DOCUMENT_ID.test(value) && Buffer.byteLength(value, 'utf8') <= MAX_DOCUMENT_ID_BYTES;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//# sourceMappingURL=document-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/document-id.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 * `isDocumentId(value)` — may this value be handed to `.doc()` as ONE opaque\n * document id (AGL-1771)?\n *\n * WHY THE QUESTION EXISTS. `CollectionReference.doc()` appends a\n * SLASH-SEPARATED path and refuses it only when the resulting component count\n * comes out odd (`@google-cloud/firestore`, `collection-reference.js`,\n * measured in `842e72576`). It never treats its argument as one opaque id. So\n * an unvalidated value taken from a request, a webhook tag, a cookie or a\n * stored field gives three distinct failures, none of which is \"not found\":\n *\n * * an ODD component count (`a/b/c`) is a perfectly legal path, so the read\n * or write lands beneath a document that does not exist — invisible to\n * every console list, because they resolve the parent first;\n * * an EVEN one (`half/path`) throws SYNCHRONOUSLY at the line building the\n * ref, which is a different failure from a rejected promise and escapes\n * any `.catch()` on one. Whether that becomes a clean 500 or rejects out\n * of the handler depends entirely on whether the ref is built inside or\n * outside the handler's `try`;\n * * a reserved `__…__` id answers `INVALID_ARGUMENT` rather than an absent\n * snapshot — the trap `542b1023f` hit with `products/__missing__`.\n *\n * WHY IT LIVES HERE. This is the third and fourth call site. AGL-1769 wrote\n * the rule as `isCartId` in `libs/plugins/commerce/src/lib/server/cart-cookie.ts`\n * and AGL-1768 copied it as a local `isDocumentId` in\n * `libs/plugins/marketing/src/lib/server/email-events.ts`; AGL-1771's sweep\n * then found two more handlers that need it. Duplication was the cheaper wait\n * at two copies and stops being cheaper at four.\n *\n * A CORRECTION, since the second copy was made for a stated reason that turns\n * out to be false: `@nx/enforce-module-boundaries` does NOT refuse an edge\n * between two feature plugins. Every plugin carries only `aglyn:addons`, and\n * that tag's rule lists `aglyn:addons` among its permitted targets — indeed\n * `campaign-send.ts` already imports `@aglyn/plugins-commerce/model` today. The\n * real objection to marketing importing commerce's cart-cookie module is not\n * lint but sense: the predicate is not about carts, and reaching it through\n * `@aglyn/plugins-commerce/server` would pull the whole commerce server graph\n * — Stripe, the billing webhook — into the marketing plugin for six lines.\n *\n * `tenant-data-admin` is the right home instead because it is where Firestore\n * paths are BUILT, and because {@link updateExisting} — the sibling primitive\n * answering the same class of question, \"must this document already exist?\" —\n * already lives beside it. Both plugins already depend on this library, so no\n * new edge is created, and this module imports nothing, so none is created\n * downstream either.\n *\n * IMPORT IT FROM THIS ENTRY POINT — `@aglyn/tenant-data-admin/server/document-id`\n * — and not from the library barrel, which is exported there only for\n * discoverability beside its siblings. Two reasons, both load-bearing:\n *\n * * this module imports NOTHING, and reaching it through `tenancy.ts` would\n * pull ~35 server modules (media CDN, erasure, Next render-cache) into\n * callers that today have no such graph. `cart-cookie.ts` imports only\n * `crypto`; it should stay that way;\n * * nearly every spec touching those callers already does\n * `jest.mock('@aglyn/tenant-data-admin', …)` precisely because that graph\n * will not load under jest. A barrel import would resolve to whatever the\n * mock object happens to contain — `undefined` at best, and at worst a\n * permissive stub that turns every path-shaped test value into a false\n * green. A leaf entry point is not intercepted, so the predicate under test\n * is always the real one.\n *\n * WHAT THIS IS NOT. Not an authorization check and not a claim that the\n * document exists. It bounds a value to one path COMPONENT; whether that\n * component names anything, and whether this caller may touch it, are separate\n * questions that only the read, the write, or a gate can answer.\n */\n\n/**\n * Firestore reserves ids matching `__…__` and rejects them with\n * `INVALID_ARGUMENT` rather than returning an absent snapshot.\n */\nconst RESERVED_DOCUMENT_ID = /^__.*__$/\n\n/** Firestore's document id ceiling, which the service counts in BYTES. */\nexport const MAX_DOCUMENT_ID_BYTES = 1500\n\n/**\n * Whether `value` is usable as ONE opaque Firestore document id.\n *\n * Every clause is a path escape rather than a style preference: `/` is both\n * the nesting hazard and the synchronous `.doc()` throw; `.` and `..` are\n * traversal that Firestore rejects outright; the reserved form throws instead\n * of reporting absence; and the length is Firestore's own limit, measured in\n * UTF-8 bytes because that is what the limit counts — 376 emoji are 1504\n * bytes and would sail through a `.length` check.\n */\nexport function isDocumentId(value: unknown): value is string {\n return (\n typeof value === 'string' &&\n value.length > 0 &&\n !value.includes('/') &&\n value !== '.' &&\n value !== '..' &&\n !RESERVED_DOCUMENT_ID.test(value) &&\n Buffer.byteLength(value, 'utf8') <= MAX_DOCUMENT_ID_BYTES\n )\n}\n"],"names":["RESERVED_DOCUMENT_ID","MAX_DOCUMENT_ID_BYTES","isDocumentId","value","length","includes","test","Buffer","byteLength"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkEC,GAED;;;CAGC,GACD,MAAMA,uBAAuB;AAE7B,wEAAwE,GACxE,OAAO,MAAMC,wBAAwB,KAAI;AAEzC;;;;;;;;;CASC,GACD,OAAO,SAASC,aAAaC,KAAc;IACzC,OACE,OAAOA,UAAU,YACjBA,MAAMC,MAAM,GAAG,KACf,CAACD,MAAME,QAAQ,CAAC,QAChBF,UAAU,OACVA,UAAU,QACV,CAACH,qBAAqBM,IAAI,CAACH,UAC3BI,OAAOC,UAAU,CAACL,OAAO,WAAWF;AAExC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
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 Vercel driver — how Aglyn's own hosting registers a name.
|
|
19
|
+
*
|
|
20
|
+
* One implementation of the call, one deadline, one never-throws contract. It
|
|
21
|
+
* was five copies before this file existed: `workspace-domains.ts`, three
|
|
22
|
+
* console routes and `subdomain-redirect.ts` each held their own `fetch` to
|
|
23
|
+
* `api.vercel.com`, which is how two copies of a constant drift apart — the
|
|
24
|
+
* bug AGL-1135 was, one layer down.
|
|
25
|
+
*
|
|
26
|
+
* ## The two projects
|
|
27
|
+
*
|
|
28
|
+
* The console and the tenant runtime are separate Vercel projects, so the
|
|
29
|
+
* scope picks the id. One token and one team serve both: a second variable for
|
|
30
|
+
* the same token is the drift this file exists to prevent.
|
|
31
|
+
*/
|
|
32
|
+
import { type DomainProvider } from './domain-provider';
|
|
33
|
+
export declare const VERCEL_DOMAIN_PROVIDER: DomainProvider;
|