@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,298 @@
|
|
|
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
|
+
* 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
|
+
*/ import { abortedDetail, domainDeadline, domainStatus, normalizeHost, redirectHostname } from "./domain-provider.js";
|
|
32
|
+
/** Mirrors the app-level canonical redirect: revocable, method-preserving. */ const REDIRECT_STATUS_CODE = 307;
|
|
33
|
+
function settingsFor(scope) {
|
|
34
|
+
const token = process.env.VERCEL_TOKEN;
|
|
35
|
+
const projectId = scope === 'tenant' ? process.env.VERCEL_TENANT_PROJECT_ID : process.env.VERCEL_CONSOLE_PROJECT_ID;
|
|
36
|
+
if (!token || !projectId) return null;
|
|
37
|
+
return {
|
|
38
|
+
token,
|
|
39
|
+
projectId,
|
|
40
|
+
teamId: process.env.VERCEL_TEAM_ID
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function query(teamId) {
|
|
44
|
+
return teamId ? `?teamId=${encodeURIComponent(teamId)}` : '';
|
|
45
|
+
}
|
|
46
|
+
function authHeaders(settings) {
|
|
47
|
+
return {
|
|
48
|
+
Authorization: `Bearer ${settings.token}`
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Attach one fully-qualified name to the scope's project.
|
|
53
|
+
*
|
|
54
|
+
* Idempotent: Vercel answers `domain_already_in_use` when the name is already
|
|
55
|
+
* on the project, which is success for our purposes — the reconcile script and
|
|
56
|
+
* the create path can both run without coordinating.
|
|
57
|
+
*
|
|
58
|
+
* ⚠️ Tolerating `domain_already_in_use` is only safe while **every** name this
|
|
59
|
+
* is asked to attach is also claimed in Firestore. Without that, a second org
|
|
60
|
+
* attaches a name someone else already holds, reads `already-exists` as
|
|
61
|
+
* success, and gets a console that looks healthy while its visitors are served
|
|
62
|
+
* — or redirected — somewhere else. That is the AGL-743 shape, and
|
|
63
|
+
* `console-domains.ts` is where the claim is kept in step.
|
|
64
|
+
*/ async function attach(scope, name, options = {}) {
|
|
65
|
+
const domain = normalizeHost(name);
|
|
66
|
+
const settings = settingsFor(scope);
|
|
67
|
+
if (!settings || !domain) return {
|
|
68
|
+
outcome: 'skipped',
|
|
69
|
+
domain
|
|
70
|
+
};
|
|
71
|
+
const redirect = options.redirectTo ? redirectHostname(options.redirectTo) : null;
|
|
72
|
+
if (options.redirectTo && !redirect) {
|
|
73
|
+
console.error('[domain-provider:vercel] refusing a non-hostname redirect', options.redirectTo);
|
|
74
|
+
return {
|
|
75
|
+
outcome: 'failed',
|
|
76
|
+
domain,
|
|
77
|
+
detail: 'invalid-redirect'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const body = redirect ? {
|
|
81
|
+
name: domain,
|
|
82
|
+
redirect,
|
|
83
|
+
redirectStatusCode: REDIRECT_STATUS_CODE
|
|
84
|
+
} : {
|
|
85
|
+
name: domain
|
|
86
|
+
};
|
|
87
|
+
try {
|
|
88
|
+
var _ref;
|
|
89
|
+
var _payload_error;
|
|
90
|
+
const response = await fetch(`https://api.vercel.com/v10/projects/${settings.projectId}/domains${query(settings.teamId)}`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: _extends({}, authHeaders(settings), {
|
|
93
|
+
'Content-Type': 'application/json'
|
|
94
|
+
}),
|
|
95
|
+
body: JSON.stringify(body),
|
|
96
|
+
signal: domainDeadline()
|
|
97
|
+
});
|
|
98
|
+
if (response.ok) return {
|
|
99
|
+
outcome: 'attached',
|
|
100
|
+
domain
|
|
101
|
+
};
|
|
102
|
+
const payload = await response.json().catch(()=>null);
|
|
103
|
+
const code = String((_ref = payload == null ? void 0 : (_payload_error = payload.error) == null ? void 0 : _payload_error.code) != null ? _ref : '');
|
|
104
|
+
if (code === 'domain_already_in_use' || response.status === 409) {
|
|
105
|
+
// An entry that already exists still has to CARRY the redirect, or the
|
|
106
|
+
// twin quietly serves the console instead of forwarding to the primary.
|
|
107
|
+
if (redirect) return patchRedirect(settings, domain, redirect);
|
|
108
|
+
return {
|
|
109
|
+
outcome: 'already-exists',
|
|
110
|
+
domain
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
console.error('[domain-provider:vercel] attach failed', domain, code);
|
|
114
|
+
return {
|
|
115
|
+
outcome: 'failed',
|
|
116
|
+
domain,
|
|
117
|
+
detail: code || String(response.status)
|
|
118
|
+
};
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('[domain-provider:vercel] attach threw', domain, error);
|
|
121
|
+
return {
|
|
122
|
+
outcome: 'failed',
|
|
123
|
+
domain,
|
|
124
|
+
detail: abortedDetail(error)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Point an already-attached name at `redirect`. Same never-throws contract. */ async function patchRedirect(settings, domain, redirect) {
|
|
129
|
+
try {
|
|
130
|
+
const response = await fetch(`https://api.vercel.com/v9/projects/${settings.projectId}/domains/${encodeURIComponent(domain)}${query(settings.teamId)}`, {
|
|
131
|
+
method: 'PATCH',
|
|
132
|
+
headers: _extends({}, authHeaders(settings), {
|
|
133
|
+
'Content-Type': 'application/json'
|
|
134
|
+
}),
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
redirect,
|
|
137
|
+
redirectStatusCode: REDIRECT_STATUS_CODE
|
|
138
|
+
}),
|
|
139
|
+
signal: domainDeadline()
|
|
140
|
+
});
|
|
141
|
+
if (response.ok) return {
|
|
142
|
+
outcome: 'already-exists',
|
|
143
|
+
domain
|
|
144
|
+
};
|
|
145
|
+
console.error('[domain-provider:vercel] redirect patch failed', domain, response.status);
|
|
146
|
+
return {
|
|
147
|
+
outcome: 'failed',
|
|
148
|
+
domain,
|
|
149
|
+
detail: String(response.status)
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('[domain-provider:vercel] redirect patch threw', domain, error);
|
|
153
|
+
return {
|
|
154
|
+
outcome: 'failed',
|
|
155
|
+
domain,
|
|
156
|
+
detail: abortedDetail(error)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function detach(scope, name) {
|
|
161
|
+
const domain = normalizeHost(name);
|
|
162
|
+
const settings = settingsFor(scope);
|
|
163
|
+
if (!settings || !domain) return {
|
|
164
|
+
outcome: 'skipped',
|
|
165
|
+
domain
|
|
166
|
+
};
|
|
167
|
+
try {
|
|
168
|
+
const response = await fetch(`https://api.vercel.com/v9/projects/${settings.projectId}/domains/${encodeURIComponent(domain)}${query(settings.teamId)}`, {
|
|
169
|
+
method: 'DELETE',
|
|
170
|
+
headers: authHeaders(settings),
|
|
171
|
+
signal: domainDeadline()
|
|
172
|
+
});
|
|
173
|
+
if (response.ok) return {
|
|
174
|
+
outcome: 'detached',
|
|
175
|
+
domain
|
|
176
|
+
};
|
|
177
|
+
if (response.status === 404) return {
|
|
178
|
+
outcome: 'not-found',
|
|
179
|
+
domain
|
|
180
|
+
};
|
|
181
|
+
console.error('[domain-provider:vercel] detach failed', domain, response.status);
|
|
182
|
+
return {
|
|
183
|
+
outcome: 'failed',
|
|
184
|
+
domain,
|
|
185
|
+
detail: String(response.status)
|
|
186
|
+
};
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error('[domain-provider:vercel] detach threw', domain, error);
|
|
189
|
+
return {
|
|
190
|
+
outcome: 'failed',
|
|
191
|
+
domain,
|
|
192
|
+
detail: abortedDetail(error)
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/** Whether any issued certificate's names cover `domain`, wildcards included. */ function certificateCovers(cns, domain) {
|
|
197
|
+
if (!Array.isArray(cns)) return false;
|
|
198
|
+
const parent = domain.split('.').slice(1).join('.');
|
|
199
|
+
return cns.some((name)=>{
|
|
200
|
+
const cn = normalizeHost(String(name != null ? name : ''));
|
|
201
|
+
return cn === domain || cn.startsWith('*.') && cn.slice(2) === parent;
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* What a name is ACTUALLY doing on the project, right now.
|
|
206
|
+
*
|
|
207
|
+
* `attach` answers "did the POST succeed", which is a different question and a
|
|
208
|
+
* much weaker one. A domain can be on the project and still serve nothing:
|
|
209
|
+
* Vercel accepts the name, then withholds routing and the certificate until
|
|
210
|
+
* ownership is proven (when the apex is registered to another Vercel account)
|
|
211
|
+
* or until DNS actually points here. Both states return a successful POST.
|
|
212
|
+
*
|
|
213
|
+
* That is the gap this closes (AGL-1913): the site custom-domain wizard
|
|
214
|
+
* reported `attached: true` off the POST alone, so "certificate is still
|
|
215
|
+
* issuing" and "this will never work" rendered as the same green chip.
|
|
216
|
+
*
|
|
217
|
+
* Three reads, cheapest first, each of which can end the walk:
|
|
218
|
+
*
|
|
219
|
+
* 1. `GET /v9/projects/{project}/domains/{name}` — 404 means the name is not
|
|
220
|
+
* on this project at all, and `verified: false` means Vercel is holding it
|
|
221
|
+
* pending the `verification` challenge it returns alongside.
|
|
222
|
+
* 2. `GET /v6/domains/{name}/config` — the platform's own DNS check.
|
|
223
|
+
* `misconfigured` is authoritative in a way our resolver check is not, and
|
|
224
|
+
* `conflicts` names the records answering for the domain that are NOT ours
|
|
225
|
+
* — the stale-A-record-shadowing-an-ALIAS case, reported rather than
|
|
226
|
+
* inferred.
|
|
227
|
+
* 3. `GET /v5/certs?domain={name}` — whether a certificate covers the name
|
|
228
|
+
* yet. Only consulted once the first two are clean, and a FAILED cert read
|
|
229
|
+
* reports `serving` rather than inventing a problem: an unreachable certs
|
|
230
|
+
* API must never turn a healthy domain amber.
|
|
231
|
+
*/ async function status(scope, name) {
|
|
232
|
+
const domain = normalizeHost(name);
|
|
233
|
+
const settings = settingsFor(scope);
|
|
234
|
+
if (!settings || !domain) return domainStatus(domain, 'skipped');
|
|
235
|
+
const headers = authHeaders(settings);
|
|
236
|
+
const team = query(settings.teamId);
|
|
237
|
+
try {
|
|
238
|
+
const attached = await fetch(`https://api.vercel.com/v9/projects/${settings.projectId}/domains/${encodeURIComponent(domain)}${team}`, {
|
|
239
|
+
headers,
|
|
240
|
+
signal: domainDeadline()
|
|
241
|
+
});
|
|
242
|
+
if (attached.status === 404) return domainStatus(domain, 'not-attached');
|
|
243
|
+
if (!attached.ok) {
|
|
244
|
+
return domainStatus(domain, 'unknown', {
|
|
245
|
+
detail: String(attached.status)
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
const payload = await attached.json().catch(()=>null);
|
|
249
|
+
if ((payload == null ? void 0 : payload.verified) === false) {
|
|
250
|
+
return domainStatus(domain, 'ownership-pending', {
|
|
251
|
+
verification: Array.isArray(payload == null ? void 0 : payload.verification) ? payload.verification : []
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
const configured = await fetch(`https://api.vercel.com/v6/domains/${encodeURIComponent(domain)}/config${team}`, {
|
|
255
|
+
headers,
|
|
256
|
+
signal: domainDeadline()
|
|
257
|
+
});
|
|
258
|
+
if (!configured.ok) {
|
|
259
|
+
return domainStatus(domain, 'unknown', {
|
|
260
|
+
detail: String(configured.status)
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const dns = await configured.json().catch(()=>null);
|
|
264
|
+
const conflicts = Array.isArray(dns == null ? void 0 : dns.conflicts) ? dns.conflicts : [];
|
|
265
|
+
if ((dns == null ? void 0 : dns.misconfigured) === true) {
|
|
266
|
+
return domainStatus(domain, 'dns-misconfigured', {
|
|
267
|
+
conflicts
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
const certs = await fetch(`https://api.vercel.com/v5/certs?domain=${encodeURIComponent(domain)}${settings.teamId ? `&teamId=${encodeURIComponent(settings.teamId)}` : ''}`, {
|
|
271
|
+
headers,
|
|
272
|
+
signal: domainDeadline()
|
|
273
|
+
});
|
|
274
|
+
// A certs read that did not answer must not turn a healthy domain amber.
|
|
275
|
+
if (!certs.ok) return domainStatus(domain, 'serving', {
|
|
276
|
+
conflicts
|
|
277
|
+
});
|
|
278
|
+
const issued = await certs.json().catch(()=>null);
|
|
279
|
+
const covered = Array.isArray(issued == null ? void 0 : issued.certs) && issued.certs.some((cert)=>certificateCovers(cert == null ? void 0 : cert.cns, domain));
|
|
280
|
+
return domainStatus(domain, covered ? 'serving' : 'certificate-pending', {
|
|
281
|
+
conflicts
|
|
282
|
+
});
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error('[domain-provider:vercel] status threw', domain, error);
|
|
285
|
+
return domainStatus(domain, 'unknown', {
|
|
286
|
+
detail: abortedDetail(error)
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
export const VERCEL_DOMAIN_PROVIDER = {
|
|
291
|
+
id: 'vercel',
|
|
292
|
+
configured: (scope)=>settingsFor(scope) !== null,
|
|
293
|
+
attach,
|
|
294
|
+
detach,
|
|
295
|
+
status
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
//# sourceMappingURL=domain-provider-vercel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/domain-provider-vercel.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 Vercel driver — how Aglyn's own hosting registers a name.\n *\n * One implementation of the call, one deadline, one never-throws contract. It\n * was five copies before this file existed: `workspace-domains.ts`, three\n * console routes and `subdomain-redirect.ts` each held their own `fetch` to\n * `api.vercel.com`, which is how two copies of a constant drift apart — the\n * bug AGL-1135 was, one layer down.\n *\n * ## The two projects\n *\n * The console and the tenant runtime are separate Vercel projects, so the\n * scope picks the id. One token and one team serve both: a second variable for\n * the same token is the drift this file exists to prevent.\n */\n\nimport {\n abortedDetail,\n domainDeadline,\n domainStatus,\n normalizeHost,\n redirectHostname,\n type DomainAttachOptions,\n type DomainProvider,\n type DomainResult,\n type DomainScope,\n type ProjectDomainStatus,\n} from './domain-provider'\n\n/** Mirrors the app-level canonical redirect: revocable, method-preserving. */\nconst REDIRECT_STATUS_CODE = 307\n\ninterface VercelSettings {\n token: string\n projectId: string\n teamId?: string\n}\n\nfunction settingsFor(scope: DomainScope): VercelSettings | null {\n const token = process.env.VERCEL_TOKEN\n const projectId =\n scope === 'tenant'\n ? process.env.VERCEL_TENANT_PROJECT_ID\n : process.env.VERCEL_CONSOLE_PROJECT_ID\n if (!token || !projectId) return null\n return { token, projectId, teamId: process.env.VERCEL_TEAM_ID }\n}\n\nfunction query(teamId?: string): string {\n return teamId ? `?teamId=${encodeURIComponent(teamId)}` : ''\n}\n\nfunction authHeaders(settings: VercelSettings): Record<string, string> {\n return { Authorization: `Bearer ${settings.token}` }\n}\n\n/**\n * Attach one fully-qualified name to the scope's project.\n *\n * Idempotent: Vercel answers `domain_already_in_use` when the name is already\n * on the project, which is success for our purposes — the reconcile script and\n * the create path can both run without coordinating.\n *\n * ⚠️ Tolerating `domain_already_in_use` is only safe while **every** name this\n * is asked to attach is also claimed in Firestore. Without that, a second org\n * attaches a name someone else already holds, reads `already-exists` as\n * success, and gets a console that looks healthy while its visitors are served\n * — or redirected — somewhere else. That is the AGL-743 shape, and\n * `console-domains.ts` is where the claim is kept in step.\n */\nasync function attach(\n scope: DomainScope,\n name: string,\n options: DomainAttachOptions = {},\n): Promise<DomainResult> {\n const domain = normalizeHost(name)\n const settings = settingsFor(scope)\n if (!settings || !domain) return { outcome: 'skipped', domain }\n\n const redirect = options.redirectTo\n ? redirectHostname(options.redirectTo)\n : null\n if (options.redirectTo && !redirect) {\n console.error('[domain-provider:vercel] refusing a non-hostname redirect', options.redirectTo)\n return { outcome: 'failed', domain, detail: 'invalid-redirect' }\n }\n const body = redirect\n ? { name: domain, redirect, redirectStatusCode: REDIRECT_STATUS_CODE }\n : { name: domain }\n\n try {\n const response = await fetch(\n `https://api.vercel.com/v10/projects/${settings.projectId}/domains${query(settings.teamId)}`,\n {\n method: 'POST',\n headers: { ...authHeaders(settings), 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: domainDeadline(),\n },\n )\n if (response.ok) return { outcome: 'attached', domain }\n const payload = await response.json().catch(() => null)\n const code = String(payload?.error?.code ?? '')\n if (code === 'domain_already_in_use' || response.status === 409) {\n // An entry that already exists still has to CARRY the redirect, or the\n // twin quietly serves the console instead of forwarding to the primary.\n if (redirect) return patchRedirect(settings, domain, redirect)\n return { outcome: 'already-exists', domain }\n }\n console.error('[domain-provider:vercel] attach failed', domain, code)\n return { outcome: 'failed', domain, detail: code || String(response.status) }\n } catch (error) {\n console.error('[domain-provider:vercel] attach threw', domain, error)\n return { outcome: 'failed', domain, detail: abortedDetail(error) }\n }\n}\n\n/** Point an already-attached name at `redirect`. Same never-throws contract. */\nasync function patchRedirect(\n settings: VercelSettings,\n domain: string,\n redirect: string,\n): Promise<DomainResult> {\n try {\n const response = await fetch(\n `https://api.vercel.com/v9/projects/${settings.projectId}/domains/${encodeURIComponent(domain)}${query(settings.teamId)}`,\n {\n method: 'PATCH',\n headers: { ...authHeaders(settings), 'Content-Type': 'application/json' },\n body: JSON.stringify({\n redirect,\n redirectStatusCode: REDIRECT_STATUS_CODE,\n }),\n signal: domainDeadline(),\n },\n )\n if (response.ok) return { outcome: 'already-exists', domain }\n console.error('[domain-provider:vercel] redirect patch failed', domain, response.status)\n return { outcome: 'failed', domain, detail: String(response.status) }\n } catch (error) {\n console.error('[domain-provider:vercel] redirect patch threw', domain, error)\n return { outcome: 'failed', domain, detail: abortedDetail(error) }\n }\n}\n\nasync function detach(\n scope: DomainScope,\n name: string,\n): Promise<DomainResult> {\n const domain = normalizeHost(name)\n const settings = settingsFor(scope)\n if (!settings || !domain) return { outcome: 'skipped', domain }\n try {\n const response = await fetch(\n `https://api.vercel.com/v9/projects/${settings.projectId}/domains/${encodeURIComponent(domain)}${query(settings.teamId)}`,\n {\n method: 'DELETE',\n headers: authHeaders(settings),\n signal: domainDeadline(),\n },\n )\n if (response.ok) return { outcome: 'detached', domain }\n if (response.status === 404) return { outcome: 'not-found', domain }\n console.error('[domain-provider:vercel] detach failed', domain, response.status)\n return { outcome: 'failed', domain, detail: String(response.status) }\n } catch (error) {\n console.error('[domain-provider:vercel] detach threw', domain, error)\n return { outcome: 'failed', domain, detail: abortedDetail(error) }\n }\n}\n\n/** Whether any issued certificate's names cover `domain`, wildcards included. */\nfunction certificateCovers(cns: unknown, domain: string): boolean {\n if (!Array.isArray(cns)) return false\n const parent = domain.split('.').slice(1).join('.')\n return cns.some((name) => {\n const cn = normalizeHost(String(name ?? ''))\n return cn === domain || (cn.startsWith('*.') && cn.slice(2) === parent)\n })\n}\n\n/**\n * What a name is ACTUALLY doing on the project, right now.\n *\n * `attach` answers \"did the POST succeed\", which is a different question and a\n * much weaker one. A domain can be on the project and still serve nothing:\n * Vercel accepts the name, then withholds routing and the certificate until\n * ownership is proven (when the apex is registered to another Vercel account)\n * or until DNS actually points here. Both states return a successful POST.\n *\n * That is the gap this closes (AGL-1913): the site custom-domain wizard\n * reported `attached: true` off the POST alone, so \"certificate is still\n * issuing\" and \"this will never work\" rendered as the same green chip.\n *\n * Three reads, cheapest first, each of which can end the walk:\n *\n * 1. `GET /v9/projects/{project}/domains/{name}` — 404 means the name is not\n * on this project at all, and `verified: false` means Vercel is holding it\n * pending the `verification` challenge it returns alongside.\n * 2. `GET /v6/domains/{name}/config` — the platform's own DNS check.\n * `misconfigured` is authoritative in a way our resolver check is not, and\n * `conflicts` names the records answering for the domain that are NOT ours\n * — the stale-A-record-shadowing-an-ALIAS case, reported rather than\n * inferred.\n * 3. `GET /v5/certs?domain={name}` — whether a certificate covers the name\n * yet. Only consulted once the first two are clean, and a FAILED cert read\n * reports `serving` rather than inventing a problem: an unreachable certs\n * API must never turn a healthy domain amber.\n */\nasync function status(\n scope: DomainScope,\n name: string,\n): Promise<ProjectDomainStatus> {\n const domain = normalizeHost(name)\n const settings = settingsFor(scope)\n if (!settings || !domain) return domainStatus(domain, 'skipped')\n const headers = authHeaders(settings)\n const team = query(settings.teamId)\n\n try {\n const attached = await fetch(\n `https://api.vercel.com/v9/projects/${settings.projectId}/domains/${encodeURIComponent(domain)}${team}`,\n { headers, signal: domainDeadline() },\n )\n if (attached.status === 404) return domainStatus(domain, 'not-attached')\n if (!attached.ok) {\n return domainStatus(domain, 'unknown', { detail: String(attached.status) })\n }\n const payload = await attached.json().catch(() => null)\n if (payload?.verified === false) {\n return domainStatus(domain, 'ownership-pending', {\n verification: Array.isArray(payload?.verification)\n ? payload.verification\n : [],\n })\n }\n\n const configured = await fetch(\n `https://api.vercel.com/v6/domains/${encodeURIComponent(domain)}/config${team}`,\n { headers, signal: domainDeadline() },\n )\n if (!configured.ok) {\n return domainStatus(domain, 'unknown', { detail: String(configured.status) })\n }\n const dns = await configured.json().catch(() => null)\n const conflicts = Array.isArray(dns?.conflicts) ? dns.conflicts : []\n if (dns?.misconfigured === true) {\n return domainStatus(domain, 'dns-misconfigured', { conflicts })\n }\n\n const certs = await fetch(\n `https://api.vercel.com/v5/certs?domain=${encodeURIComponent(domain)}${\n settings.teamId ? `&teamId=${encodeURIComponent(settings.teamId)}` : ''\n }`,\n { headers, signal: domainDeadline() },\n )\n // A certs read that did not answer must not turn a healthy domain amber.\n if (!certs.ok) return domainStatus(domain, 'serving', { conflicts })\n const issued = await certs.json().catch(() => null)\n const covered =\n Array.isArray(issued?.certs) &&\n issued.certs.some((cert: { cns?: unknown }) =>\n certificateCovers(cert?.cns, domain),\n )\n return domainStatus(domain, covered ? 'serving' : 'certificate-pending', {\n conflicts,\n })\n } catch (error) {\n console.error('[domain-provider:vercel] status threw', domain, error)\n return domainStatus(domain, 'unknown', { detail: abortedDetail(error) })\n }\n}\n\nexport const VERCEL_DOMAIN_PROVIDER: DomainProvider = {\n id: 'vercel',\n configured: (scope) => settingsFor(scope) !== null,\n attach,\n detach,\n status,\n}\n"],"names":["abortedDetail","domainDeadline","domainStatus","normalizeHost","redirectHostname","REDIRECT_STATUS_CODE","settingsFor","scope","token","process","env","VERCEL_TOKEN","projectId","VERCEL_TENANT_PROJECT_ID","VERCEL_CONSOLE_PROJECT_ID","teamId","VERCEL_TEAM_ID","query","encodeURIComponent","authHeaders","settings","Authorization","attach","name","options","domain","outcome","redirect","redirectTo","console","error","detail","body","redirectStatusCode","payload","response","fetch","method","headers","JSON","stringify","signal","ok","json","catch","code","String","status","patchRedirect","detach","certificateCovers","cns","Array","isArray","parent","split","slice","join","some","cn","startsWith","team","attached","verified","verification","configured","dns","conflicts","misconfigured","certs","issued","covered","cert","VERCEL_DOMAIN_PROVIDER","id"],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
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 webhook driver — the layer an operator supplies.
|
|
19
|
+
*
|
|
20
|
+
* Aglyn asks one endpoint the three questions in the provider contract and
|
|
21
|
+
* takes its word for the answer. What is on the other side is entirely the
|
|
22
|
+
* operator's business: Caddy's admin API, a Traefik dynamic-config file, a
|
|
23
|
+
* cert-manager `Certificate`, a registrar's API, or twenty lines of shell.
|
|
24
|
+
*
|
|
25
|
+
* This is the extension point rather than a module path the app imports,
|
|
26
|
+
* because a module path does not survive the product. Both apps are bundled by
|
|
27
|
+
* Next, so a path resolved at runtime is not in the bundle, and an operator
|
|
28
|
+
* running the published image has no build step in which to add one. A webhook
|
|
29
|
+
* needs no rebuild and works from the shipped image unchanged.
|
|
30
|
+
*
|
|
31
|
+
* ## The wire format
|
|
32
|
+
*
|
|
33
|
+
* `POST $AGLYN_DOMAIN_WEBHOOK_URL`, `Content-Type: application/json`, and
|
|
34
|
+
* `Authorization: Bearer $AGLYN_DOMAIN_WEBHOOK_TOKEN` when that is set:
|
|
35
|
+
*
|
|
36
|
+
* ```json
|
|
37
|
+
* { "action": "attach", "scope": "tenant", "domain": "shop.acme.com",
|
|
38
|
+
* "redirectTo": "acme.com" }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* `action` is `attach`, `detach` or `status`. `scope` says which app should
|
|
42
|
+
* answer for the name — `console` for the admin app and its workspace
|
|
43
|
+
* subdomains, `tenant` for published sites. `redirectTo` appears only when the
|
|
44
|
+
* name should forward rather than serve, and is always a bare hostname.
|
|
45
|
+
*
|
|
46
|
+
* Answer `200` with either an outcome:
|
|
47
|
+
*
|
|
48
|
+
* ```json
|
|
49
|
+
* { "outcome": "attached", "detail": "added to caddy" }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* or, for `status`, a state:
|
|
53
|
+
*
|
|
54
|
+
* ```json
|
|
55
|
+
* { "state": "ownership-pending",
|
|
56
|
+
* "verification": [{ "type": "TXT", "domain": "_acme-challenge.shop.acme.com",
|
|
57
|
+
* "value": "…" }] }
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* ## What Aglyn does with a bad answer
|
|
61
|
+
*
|
|
62
|
+
* An unrecognized `outcome` is `failed` and an unrecognized `state` is
|
|
63
|
+
* `unknown`, both logged with what arrived. A typo must not read as success —
|
|
64
|
+
* an endpoint that answers `{"outcome":"attach"}` has registered nothing, and
|
|
65
|
+
* accepting it would leave a workspace advertising a URL that resolves
|
|
66
|
+
* nowhere.
|
|
67
|
+
*
|
|
68
|
+
* ⚠️ Send this to a service on your own network. The request carries the
|
|
69
|
+
* bearer token and the names of your customers' domains, and the reply decides
|
|
70
|
+
* whether the console tells someone their site is live.
|
|
71
|
+
*/
|
|
72
|
+
import { type DomainProvider } from './domain-provider';
|
|
73
|
+
export declare function webhookDomainProvider(): DomainProvider;
|
|
@@ -0,0 +1,223 @@
|
|
|
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
|
+
* The webhook driver — the layer an operator supplies.
|
|
19
|
+
*
|
|
20
|
+
* Aglyn asks one endpoint the three questions in the provider contract and
|
|
21
|
+
* takes its word for the answer. What is on the other side is entirely the
|
|
22
|
+
* operator's business: Caddy's admin API, a Traefik dynamic-config file, a
|
|
23
|
+
* cert-manager `Certificate`, a registrar's API, or twenty lines of shell.
|
|
24
|
+
*
|
|
25
|
+
* This is the extension point rather than a module path the app imports,
|
|
26
|
+
* because a module path does not survive the product. Both apps are bundled by
|
|
27
|
+
* Next, so a path resolved at runtime is not in the bundle, and an operator
|
|
28
|
+
* running the published image has no build step in which to add one. A webhook
|
|
29
|
+
* needs no rebuild and works from the shipped image unchanged.
|
|
30
|
+
*
|
|
31
|
+
* ## The wire format
|
|
32
|
+
*
|
|
33
|
+
* `POST $AGLYN_DOMAIN_WEBHOOK_URL`, `Content-Type: application/json`, and
|
|
34
|
+
* `Authorization: Bearer $AGLYN_DOMAIN_WEBHOOK_TOKEN` when that is set:
|
|
35
|
+
*
|
|
36
|
+
* ```json
|
|
37
|
+
* { "action": "attach", "scope": "tenant", "domain": "shop.acme.com",
|
|
38
|
+
* "redirectTo": "acme.com" }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* `action` is `attach`, `detach` or `status`. `scope` says which app should
|
|
42
|
+
* answer for the name — `console` for the admin app and its workspace
|
|
43
|
+
* subdomains, `tenant` for published sites. `redirectTo` appears only when the
|
|
44
|
+
* name should forward rather than serve, and is always a bare hostname.
|
|
45
|
+
*
|
|
46
|
+
* Answer `200` with either an outcome:
|
|
47
|
+
*
|
|
48
|
+
* ```json
|
|
49
|
+
* { "outcome": "attached", "detail": "added to caddy" }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* or, for `status`, a state:
|
|
53
|
+
*
|
|
54
|
+
* ```json
|
|
55
|
+
* { "state": "ownership-pending",
|
|
56
|
+
* "verification": [{ "type": "TXT", "domain": "_acme-challenge.shop.acme.com",
|
|
57
|
+
* "value": "…" }] }
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* ## What Aglyn does with a bad answer
|
|
61
|
+
*
|
|
62
|
+
* An unrecognized `outcome` is `failed` and an unrecognized `state` is
|
|
63
|
+
* `unknown`, both logged with what arrived. A typo must not read as success —
|
|
64
|
+
* an endpoint that answers `{"outcome":"attach"}` has registered nothing, and
|
|
65
|
+
* accepting it would leave a workspace advertising a URL that resolves
|
|
66
|
+
* nowhere.
|
|
67
|
+
*
|
|
68
|
+
* ⚠️ Send this to a service on your own network. The request carries the
|
|
69
|
+
* bearer token and the names of your customers' domains, and the reply decides
|
|
70
|
+
* whether the console tells someone their site is live.
|
|
71
|
+
*/ import { abortedDetail, domainDeadline, domainStatus, normalizeHost, redirectHostname } from "./domain-provider.js";
|
|
72
|
+
const OUTCOMES = [
|
|
73
|
+
'attached',
|
|
74
|
+
'detached',
|
|
75
|
+
'already-exists',
|
|
76
|
+
'not-found',
|
|
77
|
+
'skipped',
|
|
78
|
+
'failed'
|
|
79
|
+
];
|
|
80
|
+
const STATES = [
|
|
81
|
+
'serving',
|
|
82
|
+
'certificate-pending',
|
|
83
|
+
'ownership-pending',
|
|
84
|
+
'dns-misconfigured',
|
|
85
|
+
'not-attached',
|
|
86
|
+
'skipped',
|
|
87
|
+
'unknown'
|
|
88
|
+
];
|
|
89
|
+
function settings() {
|
|
90
|
+
var _process_env_AGLYN_DOMAIN_WEBHOOK_URL;
|
|
91
|
+
var _process_env_AGLYN_DOMAIN_WEBHOOK_TOKEN;
|
|
92
|
+
const url = String((_process_env_AGLYN_DOMAIN_WEBHOOK_URL = process.env.AGLYN_DOMAIN_WEBHOOK_URL) != null ? _process_env_AGLYN_DOMAIN_WEBHOOK_URL : '').trim();
|
|
93
|
+
if (!url) return null;
|
|
94
|
+
return {
|
|
95
|
+
url,
|
|
96
|
+
token: ((_process_env_AGLYN_DOMAIN_WEBHOOK_TOKEN = process.env.AGLYN_DOMAIN_WEBHOOK_TOKEN) == null ? void 0 : _process_env_AGLYN_DOMAIN_WEBHOOK_TOKEN.trim()) || undefined
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
async function call(config, action, scope, domain, redirectTo) {
|
|
100
|
+
try {
|
|
101
|
+
const response = await fetch(config.url, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: _extends({
|
|
104
|
+
'Content-Type': 'application/json'
|
|
105
|
+
}, config.token ? {
|
|
106
|
+
Authorization: `Bearer ${config.token}`
|
|
107
|
+
} : {}),
|
|
108
|
+
body: JSON.stringify(_extends({
|
|
109
|
+
action,
|
|
110
|
+
scope,
|
|
111
|
+
domain
|
|
112
|
+
}, redirectTo ? {
|
|
113
|
+
redirectTo
|
|
114
|
+
} : {})),
|
|
115
|
+
signal: domainDeadline()
|
|
116
|
+
});
|
|
117
|
+
const body = await response.json().catch(()=>null);
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
console.error(`[domain-provider:webhook] ${action} answered ${response.status}`, domain);
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
body,
|
|
123
|
+
detail: String(response.status)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
ok: true,
|
|
128
|
+
body,
|
|
129
|
+
detail: ''
|
|
130
|
+
};
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(`[domain-provider:webhook] ${action} threw`, domain, error);
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
body: null,
|
|
136
|
+
detail: abortedDetail(error)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** The endpoint's `detail`, kept short — it reaches operator-facing logs. */ function detailFrom(body) {
|
|
141
|
+
const raw = body == null ? void 0 : body['detail'];
|
|
142
|
+
if (typeof raw !== 'string') return undefined;
|
|
143
|
+
const trimmed = raw.trim().slice(0, 200);
|
|
144
|
+
return trimmed || undefined;
|
|
145
|
+
}
|
|
146
|
+
function recordList(value) {
|
|
147
|
+
return Array.isArray(value) ? value : [];
|
|
148
|
+
}
|
|
149
|
+
export function webhookDomainProvider() {
|
|
150
|
+
const act = async (action, scope, name, options = {})=>{
|
|
151
|
+
const domain = normalizeHost(name);
|
|
152
|
+
const config = settings();
|
|
153
|
+
if (!config || !domain) return {
|
|
154
|
+
outcome: 'skipped',
|
|
155
|
+
domain
|
|
156
|
+
};
|
|
157
|
+
// Normalized before the wire, so an operator's endpoint receives the same
|
|
158
|
+
// bare hostname Vercel demands and no two drivers disagree on the shape.
|
|
159
|
+
const redirect = options.redirectTo ? redirectHostname(options.redirectTo) : null;
|
|
160
|
+
if (options.redirectTo && !redirect) {
|
|
161
|
+
console.error('[domain-provider:webhook] refusing a non-hostname redirect', options.redirectTo);
|
|
162
|
+
return {
|
|
163
|
+
outcome: 'failed',
|
|
164
|
+
domain,
|
|
165
|
+
detail: 'invalid-redirect'
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const { ok, body, detail } = await call(config, action, scope, domain, redirect != null ? redirect : undefined);
|
|
169
|
+
if (!ok) return {
|
|
170
|
+
outcome: 'failed',
|
|
171
|
+
domain,
|
|
172
|
+
detail: detail || 'webhook'
|
|
173
|
+
};
|
|
174
|
+
const outcome = body == null ? void 0 : body['outcome'];
|
|
175
|
+
if (typeof outcome !== 'string' || !OUTCOMES.includes(outcome)) {
|
|
176
|
+
console.error(`[domain-provider:webhook] ${action} returned an unknown outcome`, domain, outcome);
|
|
177
|
+
return {
|
|
178
|
+
outcome: 'failed',
|
|
179
|
+
domain,
|
|
180
|
+
detail: 'bad-outcome'
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return _extends({
|
|
184
|
+
outcome: outcome,
|
|
185
|
+
domain
|
|
186
|
+
}, detailFrom(body) ? {
|
|
187
|
+
detail: detailFrom(body)
|
|
188
|
+
} : {});
|
|
189
|
+
};
|
|
190
|
+
return {
|
|
191
|
+
id: 'webhook',
|
|
192
|
+
configured: ()=>settings() !== null,
|
|
193
|
+
attach: (scope, name, options)=>act('attach', scope, name, options),
|
|
194
|
+
detach: (scope, name)=>act('detach', scope, name),
|
|
195
|
+
status: async (scope, name)=>{
|
|
196
|
+
const domain = normalizeHost(name);
|
|
197
|
+
const config = settings();
|
|
198
|
+
if (!config || !domain) return domainStatus(domain, 'skipped');
|
|
199
|
+
const { ok, body, detail } = await call(config, 'status', scope, domain);
|
|
200
|
+
// Not `not-attached`: an endpoint that did not answer is not evidence
|
|
201
|
+
// that the name is missing, and reporting one as the other would strand
|
|
202
|
+
// every live domain the moment the operator's service restarted.
|
|
203
|
+
if (!ok) return domainStatus(domain, 'unknown', {
|
|
204
|
+
detail: detail || 'webhook'
|
|
205
|
+
});
|
|
206
|
+
const state = body == null ? void 0 : body['state'];
|
|
207
|
+
if (typeof state !== 'string' || !STATES.includes(state)) {
|
|
208
|
+
console.error('[domain-provider:webhook] status returned an unknown state', domain, state);
|
|
209
|
+
return domainStatus(domain, 'unknown', {
|
|
210
|
+
detail: 'bad-state'
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return domainStatus(domain, state, _extends({
|
|
214
|
+
verification: recordList(body == null ? void 0 : body['verification']),
|
|
215
|
+
conflicts: recordList(body == null ? void 0 : body['conflicts'])
|
|
216
|
+
}, detailFrom(body) ? {
|
|
217
|
+
detail: detailFrom(body)
|
|
218
|
+
} : {}));
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
//# sourceMappingURL=domain-provider-webhook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/domain-provider-webhook.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 webhook driver — the layer an operator supplies.\n *\n * Aglyn asks one endpoint the three questions in the provider contract and\n * takes its word for the answer. What is on the other side is entirely the\n * operator's business: Caddy's admin API, a Traefik dynamic-config file, a\n * cert-manager `Certificate`, a registrar's API, or twenty lines of shell.\n *\n * This is the extension point rather than a module path the app imports,\n * because a module path does not survive the product. Both apps are bundled by\n * Next, so a path resolved at runtime is not in the bundle, and an operator\n * running the published image has no build step in which to add one. A webhook\n * needs no rebuild and works from the shipped image unchanged.\n *\n * ## The wire format\n *\n * `POST $AGLYN_DOMAIN_WEBHOOK_URL`, `Content-Type: application/json`, and\n * `Authorization: Bearer $AGLYN_DOMAIN_WEBHOOK_TOKEN` when that is set:\n *\n * ```json\n * { \"action\": \"attach\", \"scope\": \"tenant\", \"domain\": \"shop.acme.com\",\n * \"redirectTo\": \"acme.com\" }\n * ```\n *\n * `action` is `attach`, `detach` or `status`. `scope` says which app should\n * answer for the name — `console` for the admin app and its workspace\n * subdomains, `tenant` for published sites. `redirectTo` appears only when the\n * name should forward rather than serve, and is always a bare hostname.\n *\n * Answer `200` with either an outcome:\n *\n * ```json\n * { \"outcome\": \"attached\", \"detail\": \"added to caddy\" }\n * ```\n *\n * or, for `status`, a state:\n *\n * ```json\n * { \"state\": \"ownership-pending\",\n * \"verification\": [{ \"type\": \"TXT\", \"domain\": \"_acme-challenge.shop.acme.com\",\n * \"value\": \"…\" }] }\n * ```\n *\n * ## What Aglyn does with a bad answer\n *\n * An unrecognized `outcome` is `failed` and an unrecognized `state` is\n * `unknown`, both logged with what arrived. A typo must not read as success —\n * an endpoint that answers `{\"outcome\":\"attach\"}` has registered nothing, and\n * accepting it would leave a workspace advertising a URL that resolves\n * nowhere.\n *\n * ⚠️ Send this to a service on your own network. The request carries the\n * bearer token and the names of your customers' domains, and the reply decides\n * whether the console tells someone their site is live.\n */\n\nimport {\n abortedDetail,\n domainDeadline,\n domainStatus,\n normalizeHost,\n redirectHostname,\n type DomainAttachOptions,\n type DomainOutcome,\n type DomainProvider,\n type DomainResult,\n type DomainScope,\n type ProjectDomainState,\n type ProjectDomainStatus,\n} from './domain-provider'\n\nconst OUTCOMES: readonly DomainOutcome[] = [\n 'attached',\n 'detached',\n 'already-exists',\n 'not-found',\n 'skipped',\n 'failed',\n]\n\nconst STATES: readonly ProjectDomainState[] = [\n 'serving',\n 'certificate-pending',\n 'ownership-pending',\n 'dns-misconfigured',\n 'not-attached',\n 'skipped',\n 'unknown',\n]\n\ninterface WebhookSettings {\n url: string\n token?: string\n}\n\nfunction settings(): WebhookSettings | null {\n const url = String(process.env.AGLYN_DOMAIN_WEBHOOK_URL ?? '').trim()\n if (!url) return null\n return { url, token: process.env.AGLYN_DOMAIN_WEBHOOK_TOKEN?.trim() || undefined }\n}\n\nasync function call(\n config: WebhookSettings,\n action: 'attach' | 'detach' | 'status',\n scope: DomainScope,\n domain: string,\n redirectTo?: string,\n): Promise<{ ok: boolean; body: Record<string, unknown> | null; detail: string }> {\n try {\n const response = await fetch(config.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(config.token ? { Authorization: `Bearer ${config.token}` } : {}),\n },\n body: JSON.stringify({\n action,\n scope,\n domain,\n ...(redirectTo ? { redirectTo } : {}),\n }),\n signal: domainDeadline(),\n })\n const body = (await response.json().catch(() => null)) as\n | Record<string, unknown>\n | null\n if (!response.ok) {\n console.error(\n `[domain-provider:webhook] ${action} answered ${response.status}`,\n domain,\n )\n return { ok: false, body, detail: String(response.status) }\n }\n return { ok: true, body, detail: '' }\n } catch (error) {\n console.error(`[domain-provider:webhook] ${action} threw`, domain, error)\n return { ok: false, body: null, detail: abortedDetail(error) }\n }\n}\n\n/** The endpoint's `detail`, kept short — it reaches operator-facing logs. */\nfunction detailFrom(body: Record<string, unknown> | null): string | undefined {\n const raw = body?.['detail']\n if (typeof raw !== 'string') return undefined\n const trimmed = raw.trim().slice(0, 200)\n return trimmed || undefined\n}\n\nfunction recordList(\n value: unknown,\n): { type?: string; name?: string; value?: string }[] {\n return Array.isArray(value) ? (value as { type?: string }[]) : []\n}\n\nexport function webhookDomainProvider(): DomainProvider {\n const act = async (\n action: 'attach' | 'detach',\n scope: DomainScope,\n name: string,\n options: DomainAttachOptions = {},\n ): Promise<DomainResult> => {\n const domain = normalizeHost(name)\n const config = settings()\n if (!config || !domain) return { outcome: 'skipped', domain }\n\n // Normalized before the wire, so an operator's endpoint receives the same\n // bare hostname Vercel demands and no two drivers disagree on the shape.\n const redirect = options.redirectTo\n ? redirectHostname(options.redirectTo)\n : null\n if (options.redirectTo && !redirect) {\n console.error(\n '[domain-provider:webhook] refusing a non-hostname redirect',\n options.redirectTo,\n )\n return { outcome: 'failed', domain, detail: 'invalid-redirect' }\n }\n\n const { ok, body, detail } = await call(\n config,\n action,\n scope,\n domain,\n redirect ?? undefined,\n )\n if (!ok) return { outcome: 'failed', domain, detail: detail || 'webhook' }\n\n const outcome = body?.['outcome']\n if (typeof outcome !== 'string' || !OUTCOMES.includes(outcome as DomainOutcome)) {\n console.error(\n `[domain-provider:webhook] ${action} returned an unknown outcome`,\n domain,\n outcome,\n )\n return { outcome: 'failed', domain, detail: 'bad-outcome' }\n }\n return {\n outcome: outcome as DomainOutcome,\n domain,\n ...(detailFrom(body) ? { detail: detailFrom(body) } : {}),\n }\n }\n\n return {\n id: 'webhook',\n configured: () => settings() !== null,\n attach: (scope, name, options) => act('attach', scope, name, options),\n detach: (scope, name) => act('detach', scope, name),\n status: async (scope, name): Promise<ProjectDomainStatus> => {\n const domain = normalizeHost(name)\n const config = settings()\n if (!config || !domain) return domainStatus(domain, 'skipped')\n\n const { ok, body, detail } = await call(config, 'status', scope, domain)\n // Not `not-attached`: an endpoint that did not answer is not evidence\n // that the name is missing, and reporting one as the other would strand\n // every live domain the moment the operator's service restarted.\n if (!ok) return domainStatus(domain, 'unknown', { detail: detail || 'webhook' })\n\n const state = body?.['state']\n if (\n typeof state !== 'string' ||\n !STATES.includes(state as ProjectDomainState)\n ) {\n console.error(\n '[domain-provider:webhook] status returned an unknown state',\n domain,\n state,\n )\n return domainStatus(domain, 'unknown', { detail: 'bad-state' })\n }\n return domainStatus(domain, state as ProjectDomainState, {\n verification: recordList(body?.['verification']) as never,\n conflicts: recordList(body?.['conflicts']),\n ...(detailFrom(body) ? { detail: detailFrom(body) } : {}),\n })\n },\n }\n}\n"],"names":["abortedDetail","domainDeadline","domainStatus","normalizeHost","redirectHostname","OUTCOMES","STATES","settings","process","url","String","env","AGLYN_DOMAIN_WEBHOOK_URL","trim","token","AGLYN_DOMAIN_WEBHOOK_TOKEN","undefined","call","config","action","scope","domain","redirectTo","response","fetch","method","headers","Authorization","body","JSON","stringify","signal","json","catch","ok","console","error","status","detail","detailFrom","raw","trimmed","slice","recordList","value","Array","isArray","webhookDomainProvider","act","name","options","outcome","redirect","includes","id","configured","attach","detach","state","verification","conflicts"],"mappings":"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"}
|