@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.
Files changed (374) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +7 -0
  3. package/jest.integration.config.d.ts +39 -0
  4. package/jest.integration.setup.d.ts +17 -0
  5. package/package.json +51 -0
  6. package/src/index.d.ts +17 -0
  7. package/src/index.js +18 -0
  8. package/src/index.js.map +1 -0
  9. package/src/lib/render-cache.d.ts +184 -0
  10. package/src/lib/render-cache.js +198 -0
  11. package/src/lib/render-cache.js.map +1 -0
  12. package/src/lib/server/account-addresses.d.ts +160 -0
  13. package/src/lib/server/account-addresses.js +311 -0
  14. package/src/lib/server/account-addresses.js.map +1 -0
  15. package/src/lib/server/account-emails.d.ts +219 -0
  16. package/src/lib/server/account-emails.js +640 -0
  17. package/src/lib/server/account-emails.js.map +1 -0
  18. package/src/lib/server/admin-audit.d.ts +180 -0
  19. package/src/lib/server/admin-audit.js +233 -0
  20. package/src/lib/server/admin-audit.js.map +1 -0
  21. package/src/lib/server/analytics-retention.d.ts +63 -0
  22. package/src/lib/server/analytics-retention.js +66 -0
  23. package/src/lib/server/analytics-retention.js.map +1 -0
  24. package/src/lib/server/api-http.d.ts +159 -0
  25. package/src/lib/server/api-http.js +230 -0
  26. package/src/lib/server/api-http.js.map +1 -0
  27. package/src/lib/server/api-keys.d.ts +117 -0
  28. package/src/lib/server/api-keys.js +298 -0
  29. package/src/lib/server/api-keys.js.map +1 -0
  30. package/src/lib/server/auth-handoff.d.ts +165 -0
  31. package/src/lib/server/auth-handoff.js +482 -0
  32. package/src/lib/server/auth-handoff.js.map +1 -0
  33. package/src/lib/server/auth-pools.d.ts +246 -0
  34. package/src/lib/server/auth-pools.js +551 -0
  35. package/src/lib/server/auth-pools.js.map +1 -0
  36. package/src/lib/server/billing-addons.d.ts +171 -0
  37. package/src/lib/server/billing-addons.js +277 -0
  38. package/src/lib/server/billing-addons.js.map +1 -0
  39. package/src/lib/server/campaign-attribution-store.d.ts +55 -0
  40. package/src/lib/server/campaign-attribution-store.js +95 -0
  41. package/src/lib/server/campaign-attribution-store.js.map +1 -0
  42. package/src/lib/server/campaign-conversion-attribution.d.ts +205 -0
  43. package/src/lib/server/campaign-conversion-attribution.js +279 -0
  44. package/src/lib/server/campaign-conversion-attribution.js.map +1 -0
  45. package/src/lib/server/client-error-report.d.ts +301 -0
  46. package/src/lib/server/client-error-report.js +755 -0
  47. package/src/lib/server/client-error-report.js.map +1 -0
  48. package/src/lib/server/connect-account-status.d.ts +74 -0
  49. package/src/lib/server/connect-account-status.js +94 -0
  50. package/src/lib/server/connect-account-status.js.map +1 -0
  51. package/src/lib/server/connect-payout-failure.d.ts +103 -0
  52. package/src/lib/server/connect-payout-failure.js +127 -0
  53. package/src/lib/server/connect-payout-failure.js.map +1 -0
  54. package/src/lib/server/console-api-rate-limit.d.ts +81 -0
  55. package/src/lib/server/console-api-rate-limit.js +111 -0
  56. package/src/lib/server/console-api-rate-limit.js.map +1 -0
  57. package/src/lib/server/console-domains.d.ts +326 -0
  58. package/src/lib/server/console-domains.js +779 -0
  59. package/src/lib/server/console-domains.js.map +1 -0
  60. package/src/lib/server/consume-once.d.ts +91 -0
  61. package/src/lib/server/consume-once.js +96 -0
  62. package/src/lib/server/consume-once.js.map +1 -0
  63. package/src/lib/server/contact-company-link.d.ts +85 -0
  64. package/src/lib/server/contact-company-link.js +105 -0
  65. package/src/lib/server/contact-company-link.js.map +1 -0
  66. package/src/lib/server/contact-email-engagement.d.ts +32 -0
  67. package/src/lib/server/contact-email-engagement.js +145 -0
  68. package/src/lib/server/contact-email-engagement.js.map +1 -0
  69. package/src/lib/server/contact-email-index.d.ts +56 -0
  70. package/src/lib/server/contact-email-index.js +158 -0
  71. package/src/lib/server/contact-email-index.js.map +1 -0
  72. package/src/lib/server/contact-lifecycle-floor.d.ts +89 -0
  73. package/src/lib/server/contact-lifecycle-floor.js +88 -0
  74. package/src/lib/server/contact-lifecycle-floor.js.map +1 -0
  75. package/src/lib/server/contact-merge.d.ts +55 -0
  76. package/src/lib/server/contact-merge.js +279 -0
  77. package/src/lib/server/contact-merge.js.map +1 -0
  78. package/src/lib/server/contact-suppression.d.ts +153 -0
  79. package/src/lib/server/contact-suppression.js +259 -0
  80. package/src/lib/server/contact-suppression.js.map +1 -0
  81. package/src/lib/server/crm-booking-activity.d.ts +86 -0
  82. package/src/lib/server/crm-booking-activity.js +251 -0
  83. package/src/lib/server/crm-booking-activity.js.map +1 -0
  84. package/src/lib/server/crm-email-activity.d.ts +112 -0
  85. package/src/lib/server/crm-email-activity.js +143 -0
  86. package/src/lib/server/crm-email-activity.js.map +1 -0
  87. package/src/lib/server/crm-inbound-email.d.ts +145 -0
  88. package/src/lib/server/crm-inbound-email.js +287 -0
  89. package/src/lib/server/crm-inbound-email.js.map +1 -0
  90. package/src/lib/server/crm-next-activity.d.ts +74 -0
  91. package/src/lib/server/crm-next-activity.js +194 -0
  92. package/src/lib/server/crm-next-activity.js.map +1 -0
  93. package/src/lib/server/crm-records.d.ts +144 -0
  94. package/src/lib/server/crm-records.js +218 -0
  95. package/src/lib/server/crm-records.js.map +1 -0
  96. package/src/lib/server/csp-aggregate.d.ts +62 -0
  97. package/src/lib/server/csp-aggregate.js +276 -0
  98. package/src/lib/server/csp-aggregate.js.map +1 -0
  99. package/src/lib/server/data-storage-gate.d.ts +66 -0
  100. package/src/lib/server/data-storage-gate.js +48 -0
  101. package/src/lib/server/data-storage-gate.js.map +1 -0
  102. package/src/lib/server/dataset-live-pages.d.ts +179 -0
  103. package/src/lib/server/dataset-live-pages.js +421 -0
  104. package/src/lib/server/dataset-live-pages.js.map +1 -0
  105. package/src/lib/server/dns-probe.d.ts +53 -0
  106. package/src/lib/server/dns-probe.js +184 -0
  107. package/src/lib/server/dns-probe.js.map +1 -0
  108. package/src/lib/server/document-id.d.ts +29 -0
  109. package/src/lib/server/document-id.js +100 -0
  110. package/src/lib/server/document-id.js.map +1 -0
  111. package/src/lib/server/domain-provider-vercel.d.ts +33 -0
  112. package/src/lib/server/domain-provider-vercel.js +298 -0
  113. package/src/lib/server/domain-provider-vercel.js.map +1 -0
  114. package/src/lib/server/domain-provider-webhook.d.ts +73 -0
  115. package/src/lib/server/domain-provider-webhook.js +223 -0
  116. package/src/lib/server/domain-provider-webhook.js.map +1 -0
  117. package/src/lib/server/domain-provider-wildcard.d.ts +54 -0
  118. package/src/lib/server/domain-provider-wildcard.js +163 -0
  119. package/src/lib/server/domain-provider-wildcard.js.map +1 -0
  120. package/src/lib/server/domain-provider.d.ts +197 -0
  121. package/src/lib/server/domain-provider.js +221 -0
  122. package/src/lib/server/domain-provider.js.map +1 -0
  123. package/src/lib/server/duplicate-activity.d.ts +54 -0
  124. package/src/lib/server/duplicate-activity.js +67 -0
  125. package/src/lib/server/duplicate-activity.js.map +1 -0
  126. package/src/lib/server/duplicate-resource.d.ts +55 -0
  127. package/src/lib/server/duplicate-resource.js +474 -0
  128. package/src/lib/server/duplicate-resource.js.map +1 -0
  129. package/src/lib/server/dynamic-list-materialize.d.ts +153 -0
  130. package/src/lib/server/dynamic-list-materialize.js +581 -0
  131. package/src/lib/server/dynamic-list-materialize.js.map +1 -0
  132. package/src/lib/server/edit-access-authz.d.ts +56 -0
  133. package/src/lib/server/edit-access-authz.js +107 -0
  134. package/src/lib/server/edit-access-authz.js.map +1 -0
  135. package/src/lib/server/edit-access-token.d.ts +72 -0
  136. package/src/lib/server/edit-access-token.js +116 -0
  137. package/src/lib/server/edit-access-token.js.map +1 -0
  138. package/src/lib/server/edit-hint-token.d.ts +83 -0
  139. package/src/lib/server/edit-hint-token.js +119 -0
  140. package/src/lib/server/edit-hint-token.js.map +1 -0
  141. package/src/lib/server/email-delivery-log.d.ts +509 -0
  142. package/src/lib/server/email-delivery-log.js +1043 -0
  143. package/src/lib/server/email-delivery-log.js.map +1 -0
  144. package/src/lib/server/email-flow-gate.d.ts +80 -0
  145. package/src/lib/server/email-flow-gate.js +153 -0
  146. package/src/lib/server/email-flow-gate.js.map +1 -0
  147. package/src/lib/server/email-marketing-gate.d.ts +333 -0
  148. package/src/lib/server/email-marketing-gate.js +639 -0
  149. package/src/lib/server/email-marketing-gate.js.map +1 -0
  150. package/src/lib/server/email-metering.d.ts +293 -0
  151. package/src/lib/server/email-metering.js +359 -0
  152. package/src/lib/server/email-metering.js.map +1 -0
  153. package/src/lib/server/email-revenue-attribution.d.ts +185 -0
  154. package/src/lib/server/email-revenue-attribution.js +300 -0
  155. package/src/lib/server/email-revenue-attribution.js.map +1 -0
  156. package/src/lib/server/email-send-rate.d.ts +232 -0
  157. package/src/lib/server/email-send-rate.js +417 -0
  158. package/src/lib/server/email-send-rate.js.map +1 -0
  159. package/src/lib/server/email-sender-reputation.d.ts +317 -0
  160. package/src/lib/server/email-sender-reputation.js +464 -0
  161. package/src/lib/server/email-sender-reputation.js.map +1 -0
  162. package/src/lib/server/email-suppression.d.ts +490 -0
  163. package/src/lib/server/email-suppression.js +648 -0
  164. package/src/lib/server/email-suppression.js.map +1 -0
  165. package/src/lib/server/email-topic-confirmation.d.ts +150 -0
  166. package/src/lib/server/email-topic-confirmation.js +272 -0
  167. package/src/lib/server/email-topic-confirmation.js.map +1 -0
  168. package/src/lib/server/email-unsubscribe-link.d.ts +174 -0
  169. package/src/lib/server/email-unsubscribe-link.js +236 -0
  170. package/src/lib/server/email-unsubscribe-link.js.map +1 -0
  171. package/src/lib/server/erase-person.d.ts +116 -0
  172. package/src/lib/server/erase-person.js +272 -0
  173. package/src/lib/server/erase-person.js.map +1 -0
  174. package/src/lib/server/erase.d.ts +458 -0
  175. package/src/lib/server/erase.js +1506 -0
  176. package/src/lib/server/erase.js.map +1 -0
  177. package/src/lib/server/firebase-admin.d.ts +71 -0
  178. package/src/lib/server/firebase-admin.js +343 -0
  179. package/src/lib/server/firebase-admin.js.map +1 -0
  180. package/src/lib/server/form-dataset-binding-token.d.ts +35 -0
  181. package/src/lib/server/form-dataset-binding-token.js +107 -0
  182. package/src/lib/server/form-dataset-binding-token.js.map +1 -0
  183. package/src/lib/server/free-workspace-cap.d.ts +201 -0
  184. package/src/lib/server/free-workspace-cap.js +404 -0
  185. package/src/lib/server/free-workspace-cap.js.map +1 -0
  186. package/src/lib/server/ga4-measurement-protocol.d.ts +256 -0
  187. package/src/lib/server/ga4-measurement-protocol.js +558 -0
  188. package/src/lib/server/ga4-measurement-protocol.js.map +1 -0
  189. package/src/lib/server/host-email-tokens.d.ts +47 -0
  190. package/src/lib/server/host-email-tokens.js +72 -0
  191. package/src/lib/server/host-email-tokens.js.map +1 -0
  192. package/src/lib/server/host-memberships.d.ts +54 -0
  193. package/src/lib/server/host-memberships.js +194 -0
  194. package/src/lib/server/host-memberships.js.map +1 -0
  195. package/src/lib/server/host-sending-domain.d.ts +292 -0
  196. package/src/lib/server/host-sending-domain.js +451 -0
  197. package/src/lib/server/host-sending-domain.js.map +1 -0
  198. package/src/lib/server/host-visitor-records.d.ts +150 -0
  199. package/src/lib/server/host-visitor-records.js +262 -0
  200. package/src/lib/server/host-visitor-records.js.map +1 -0
  201. package/src/lib/server/id-token-refusal.d.ts +28 -0
  202. package/src/lib/server/id-token-refusal.js +111 -0
  203. package/src/lib/server/id-token-refusal.js.map +1 -0
  204. package/src/lib/server/legal-acceptance.d.ts +214 -0
  205. package/src/lib/server/legal-acceptance.js +305 -0
  206. package/src/lib/server/legal-acceptance.js.map +1 -0
  207. package/src/lib/server/list-members.d.ts +211 -0
  208. package/src/lib/server/list-members.js +176 -0
  209. package/src/lib/server/list-members.js.map +1 -0
  210. package/src/lib/server/live-page-usage.d.ts +202 -0
  211. package/src/lib/server/live-page-usage.js +300 -0
  212. package/src/lib/server/live-page-usage.js.map +1 -0
  213. package/src/lib/server/lockdown.d.ts +313 -0
  214. package/src/lib/server/lockdown.js +643 -0
  215. package/src/lib/server/lockdown.js.map +1 -0
  216. package/src/lib/server/media-cdn-rate-limit.d.ts +188 -0
  217. package/src/lib/server/media-cdn-rate-limit.js +204 -0
  218. package/src/lib/server/media-cdn-rate-limit.js.map +1 -0
  219. package/src/lib/server/media-delivery.d.ts +267 -0
  220. package/src/lib/server/media-delivery.js +485 -0
  221. package/src/lib/server/media-delivery.js.map +1 -0
  222. package/src/lib/server/media-download-tokens.d.ts +161 -0
  223. package/src/lib/server/media-download-tokens.js +386 -0
  224. package/src/lib/server/media-download-tokens.js.map +1 -0
  225. package/src/lib/server/media-quarantine.d.ts +127 -0
  226. package/src/lib/server/media-quarantine.js +170 -0
  227. package/src/lib/server/media-quarantine.js.map +1 -0
  228. package/src/lib/server/media-signing.d.ts +126 -0
  229. package/src/lib/server/media-signing.js +159 -0
  230. package/src/lib/server/media-signing.js.map +1 -0
  231. package/src/lib/server/media-storage-path.d.ts +96 -0
  232. package/src/lib/server/media-storage-path.js +111 -0
  233. package/src/lib/server/media-storage-path.js.map +1 -0
  234. package/src/lib/server/media-strong-digest.d.ts +85 -0
  235. package/src/lib/server/media-strong-digest.js +190 -0
  236. package/src/lib/server/media-strong-digest.js.map +1 -0
  237. package/src/lib/server/media-tombstone.d.ts +276 -0
  238. package/src/lib/server/media-tombstone.js +421 -0
  239. package/src/lib/server/media-tombstone.js.map +1 -0
  240. package/src/lib/server/media-variants.d.ts +309 -0
  241. package/src/lib/server/media-variants.js +428 -0
  242. package/src/lib/server/media-variants.js.map +1 -0
  243. package/src/lib/server/member-email-aliases.d.ts +180 -0
  244. package/src/lib/server/member-email-aliases.js +420 -0
  245. package/src/lib/server/member-email-aliases.js.map +1 -0
  246. package/src/lib/server/member-photo.d.ts +88 -0
  247. package/src/lib/server/member-photo.js +185 -0
  248. package/src/lib/server/member-photo.js.map +1 -0
  249. package/src/lib/server/membership-recover-throttle.d.ts +211 -0
  250. package/src/lib/server/membership-recover-throttle.js +231 -0
  251. package/src/lib/server/membership-recover-throttle.js.map +1 -0
  252. package/src/lib/server/notifications.d.ts +55 -0
  253. package/src/lib/server/notifications.js +134 -0
  254. package/src/lib/server/notifications.js.map +1 -0
  255. package/src/lib/server/org-billing.d.ts +97 -0
  256. package/src/lib/server/org-billing.js +236 -0
  257. package/src/lib/server/org-billing.js.map +1 -0
  258. package/src/lib/server/organizations.d.ts +837 -0
  259. package/src/lib/server/organizations.js +1814 -0
  260. package/src/lib/server/organizations.js.map +1 -0
  261. package/src/lib/server/password-reset-throttle.d.ts +84 -0
  262. package/src/lib/server/password-reset-throttle.js +102 -0
  263. package/src/lib/server/password-reset-throttle.js.map +1 -0
  264. package/src/lib/server/personal-data-export.d.ts +241 -0
  265. package/src/lib/server/personal-data-export.js +858 -0
  266. package/src/lib/server/personal-data-export.js.map +1 -0
  267. package/src/lib/server/platform-domain-names.d.ts +62 -0
  268. package/src/lib/server/platform-domain-names.js +197 -0
  269. package/src/lib/server/platform-domain-names.js.map +1 -0
  270. package/src/lib/server/platform-marketing-consent.d.ts +133 -0
  271. package/src/lib/server/platform-marketing-consent.js +209 -0
  272. package/src/lib/server/platform-marketing-consent.js.map +1 -0
  273. package/src/lib/server/rate-limit-store.d.ts +702 -0
  274. package/src/lib/server/rate-limit-store.js +1220 -0
  275. package/src/lib/server/rate-limit-store.js.map +1 -0
  276. package/src/lib/server/realm-plugins.d.ts +67 -0
  277. package/src/lib/server/realm-plugins.js +250 -0
  278. package/src/lib/server/realm-plugins.js.map +1 -0
  279. package/src/lib/server/recaptcha-allowlist.d.ts +90 -0
  280. package/src/lib/server/recaptcha-allowlist.js +409 -0
  281. package/src/lib/server/recaptcha-allowlist.js.map +1 -0
  282. package/src/lib/server/release-flags.d.ts +105 -0
  283. package/src/lib/server/release-flags.js +183 -0
  284. package/src/lib/server/release-flags.js.map +1 -0
  285. package/src/lib/server/replay-erasures.d.ts +126 -0
  286. package/src/lib/server/replay-erasures.js +217 -0
  287. package/src/lib/server/replay-erasures.js.map +1 -0
  288. package/src/lib/server/resolve-people.d.ts +66 -0
  289. package/src/lib/server/resolve-people.js +112 -0
  290. package/src/lib/server/resolve-people.js.map +1 -0
  291. package/src/lib/server/safe-equal.d.ts +45 -0
  292. package/src/lib/server/safe-equal.js +58 -0
  293. package/src/lib/server/safe-equal.js.map +1 -0
  294. package/src/lib/server/sending-domain-debt.d.ts +127 -0
  295. package/src/lib/server/sending-domain-debt.js +268 -0
  296. package/src/lib/server/sending-domain-debt.js.map +1 -0
  297. package/src/lib/server/sending-domain-recheck.d.ts +151 -0
  298. package/src/lib/server/sending-domain-recheck.js +253 -0
  299. package/src/lib/server/sending-domain-recheck.js.map +1 -0
  300. package/src/lib/server/sending-domains.d.ts +364 -0
  301. package/src/lib/server/sending-domains.js +683 -0
  302. package/src/lib/server/sending-domains.js.map +1 -0
  303. package/src/lib/server/serve-media-cdn.d.ts +433 -0
  304. package/src/lib/server/serve-media-cdn.js +1370 -0
  305. package/src/lib/server/serve-media-cdn.js.map +1 -0
  306. package/src/lib/server/serve-plugin-fetch.d.ts +61 -0
  307. package/src/lib/server/serve-plugin-fetch.js +294 -0
  308. package/src/lib/server/serve-plugin-fetch.js.map +1 -0
  309. package/src/lib/server/sms-keywords.d.ts +63 -0
  310. package/src/lib/server/sms-keywords.js +146 -0
  311. package/src/lib/server/sms-keywords.js.map +1 -0
  312. package/src/lib/server/sso-break-glass-owners.d.ts +91 -0
  313. package/src/lib/server/sso-break-glass-owners.js +227 -0
  314. package/src/lib/server/sso-break-glass-owners.js.map +1 -0
  315. package/src/lib/server/sso-domain-policy.d.ts +140 -0
  316. package/src/lib/server/sso-domain-policy.js +239 -0
  317. package/src/lib/server/sso-domain-policy.js.map +1 -0
  318. package/src/lib/server/sso-drift-logic.d.ts +106 -0
  319. package/src/lib/server/sso-drift-logic.js +95 -0
  320. package/src/lib/server/sso-drift-logic.js.map +1 -0
  321. package/src/lib/server/sso-enforcement.d.ts +204 -0
  322. package/src/lib/server/sso-enforcement.js +309 -0
  323. package/src/lib/server/sso-enforcement.js.map +1 -0
  324. package/src/lib/server/sso-provisioning.d.ts +320 -0
  325. package/src/lib/server/sso-provisioning.js +715 -0
  326. package/src/lib/server/sso-provisioning.js.map +1 -0
  327. package/src/lib/server/staff-alert-email.d.ts +41 -0
  328. package/src/lib/server/staff-alert-email.js +63 -0
  329. package/src/lib/server/staff-alert-email.js.map +1 -0
  330. package/src/lib/server/stripe-account-mode.d.ts +156 -0
  331. package/src/lib/server/stripe-account-mode.js +162 -0
  332. package/src/lib/server/stripe-account-mode.js.map +1 -0
  333. package/src/lib/server/tenant-write-lockdown.d.ts +131 -0
  334. package/src/lib/server/tenant-write-lockdown.js +176 -0
  335. package/src/lib/server/tenant-write-lockdown.js.map +1 -0
  336. package/src/lib/server/test-firestore.d.ts +45 -0
  337. package/src/lib/server/test-firestore.js +124 -0
  338. package/src/lib/server/test-firestore.js.map +1 -0
  339. package/src/lib/server/token-revocation.d.ts +140 -0
  340. package/src/lib/server/token-revocation.js +255 -0
  341. package/src/lib/server/token-revocation.js.map +1 -0
  342. package/src/lib/server/update-existing.d.ts +32 -0
  343. package/src/lib/server/update-existing.js +73 -0
  344. package/src/lib/server/update-existing.js.map +1 -0
  345. package/src/lib/server/upload-cors-reconcile.d.ts +118 -0
  346. package/src/lib/server/upload-cors-reconcile.js +234 -0
  347. package/src/lib/server/upload-cors-reconcile.js.map +1 -0
  348. package/src/lib/server/upsert-contact.d.ts +262 -0
  349. package/src/lib/server/upsert-contact.js +546 -0
  350. package/src/lib/server/upsert-contact.js.map +1 -0
  351. package/src/lib/server/usage-invoice.d.ts +203 -0
  352. package/src/lib/server/usage-invoice.js +397 -0
  353. package/src/lib/server/usage-invoice.js.map +1 -0
  354. package/src/lib/server/user-profiles.d.ts +151 -0
  355. package/src/lib/server/user-profiles.js +364 -0
  356. package/src/lib/server/user-profiles.js.map +1 -0
  357. package/src/lib/server/vercel-drain-signature.d.ts +30 -0
  358. package/src/lib/server/vercel-drain-signature.js +61 -0
  359. package/src/lib/server/vercel-drain-signature.js.map +1 -0
  360. package/src/lib/server/vercel-log-drain.d.ts +259 -0
  361. package/src/lib/server/vercel-log-drain.js +479 -0
  362. package/src/lib/server/vercel-log-drain.js.map +1 -0
  363. package/src/lib/server/verify-email-cooldown.d.ts +102 -0
  364. package/src/lib/server/verify-email-cooldown.js +114 -0
  365. package/src/lib/server/verify-email-cooldown.js.map +1 -0
  366. package/src/lib/server/visitor-write-rate-limit.d.ts +87 -0
  367. package/src/lib/server/visitor-write-rate-limit.js +117 -0
  368. package/src/lib/server/visitor-write-rate-limit.js.map +1 -0
  369. package/src/lib/server/workspace-domains.d.ts +156 -0
  370. package/src/lib/server/workspace-domains.js +271 -0
  371. package/src/lib/server/workspace-domains.js.map +1 -0
  372. package/src/tenancy.d.ts +115 -0
  373. package/src/tenancy.js +142 -0
  374. package/src/tenancy.js.map +1 -0
@@ -0,0 +1,451 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * THE PER-SITE SENDING DOMAIN — the claim, and nothing else.
18
+ *
19
+ * Provisioning a site's sending domain is two writes to two vendors and one
20
+ * write of our own. This module owns only the third: the LABEL CLAIM and the
21
+ * record that says a domain has been asked for. It calls no vendor, reads no
22
+ * credential, and is safe for the tenant runtime to import — which matters,
23
+ * because the tenant runtime is where a site first discovers it needs to send.
24
+ *
25
+ * The vendor half lives in the console (`provision-sending-domain.ts`), for
26
+ * the same structural reason `sending-domain-provider.ts` does: the keys that
27
+ * create a Resend domain and write our DNS zone must not be reachable from a
28
+ * process that serves published sites.
29
+ *
30
+ * ## Request here, issue there
31
+ *
32
+ * That split is what makes provisioning ON DEMAND possible at all.
33
+ * {@link requestHostSendingDomain} is a cheap Firestore write with no
34
+ * credential behind it; the console-side sweep picks the claim up and does the
35
+ * vendor work, and the re-check sweep then moves the domain to `verified` on
36
+ * its own.
37
+ *
38
+ * So a site is never blocked waiting on a credential it cannot hold, and the
39
+ * credential is never held by a process that should not have it.
40
+ *
41
+ * ## NOTHING HERE RUNS WITHOUT SOMEBODY ASKING
42
+ *
43
+ * A dedicated subdomain is the one shape in the sending model that draws on a
44
+ * resource we cannot buy our way out of at scale: a slot in the provider's
45
+ * account-wide domain allowance, three records in our own zone, and a
46
+ * permanent place in the re-verification sweep. The shared pool is flat at any
47
+ * number of sites, and a domain the CUSTOMER owns costs our zone nothing at
48
+ * all — so this is the only one that has to be rationed, and the only one a
49
+ * ceiling can bind.
50
+ *
51
+ * It used to be claimed automatically at three moments: site creation, the
52
+ * billing webhook's upgrade transition, and a sweep that walked hosts looking
53
+ * for entitled sites without one. All three made the count grow with paying
54
+ * customers rather than with anybody's decision, and a merchant who never
55
+ * wanted an Aglyn-branded sending name still spent a slot on one.
56
+ *
57
+ * The function is named for a REQUEST rather than for an invariant on purpose.
58
+ * `ensure…` is an invitation to call it defensively — "just make sure it
59
+ * exists" — which is exactly how the three automatic callers came to be, and
60
+ * `requestedBy` cannot be supplied by a caller that has nobody to name. A
61
+ * caller that finds itself inventing a value for it is a caller that should
62
+ * not be claiming.
63
+ *
64
+ * ## The label is pinned, and a rename does not move it
65
+ *
66
+ * `hosts/{hostId}.sendingLabel` is written ONCE and never rewritten. A site's
67
+ * `subdomain` is mutable — the rename route rewrites that field in place — and
68
+ * a sending domain derived from it would move every time a merchant renamed
69
+ * their site, discarding the accumulated sending reputation that is the whole
70
+ * point of having a domain per tenant, and stranding the return path that mail
71
+ * already in flight will bounce to.
72
+ *
73
+ * The web address follows the slug. The sending identity does not. A merchant
74
+ * who genuinely wants a different sending name gets
75
+ * {@link restartHostSendingDomain}, which says plainly that it starts
76
+ * reputation over — never a silent side effect of renaming.
77
+ *
78
+ * The label is proposed from the site's subdomain AT THE MOMENT OF THE
79
+ * REQUEST, which is the name the merchant is looking at when they ask. It was
80
+ * previously the creation-time slug, because the claim happened at creation
81
+ * and there was no later moment to read; a name derived from what the site is
82
+ * called today is the less surprising of the two, and the pin is what makes it
83
+ * stable from then on either way.
84
+ */ import { isPlatformSendingDomain, mailLabelCandidate, normalizeSendingDomain, platformSendingApex, platformSendingDomainFor, platformSendingLabel } from "@aglyn/shared-util-email";
85
+ import { holdsDedicatedSendingDomain } from "@aglyn/aglyn/app-utils/dedicated-sending-domain";
86
+ import firebaseAdmin from "./firebase-admin.js";
87
+ import { SENDING_DOMAINS_COLLECTION, readSendingDomainRecord } from "./sending-domains.js";
88
+ const firestore = ()=>firebaseAdmin.app().firestore();
89
+ /**
90
+ * Whether this org's sites may hold a dedicated domain.
91
+ *
92
+ * One read, and only ever on a CLAIM path — never on a send. A site that does
93
+ * not qualify has a sending identity regardless (the shared pool), so nothing
94
+ * here is on the critical path of a message.
95
+ *
96
+ * The WHOLE org document goes to the gate rather than its `plan` field, and
97
+ * that is what makes a per-org grant real: the entitlement resolves the plan's
98
+ * default and then the org's own overrides, so an account staff have granted
99
+ * one may claim without being moved up a tier — and an account whose
100
+ * subscription has died may not, because `resolveEffectivePlan` reads a dead
101
+ * subscription down to `free`. A single field would have hidden both.
102
+ *
103
+ * Fails closed on a read error, and that is the safe direction: the cost of
104
+ * answering `false` wrongly is a request the merchant makes again, while the
105
+ * cost of answering `true` wrongly is a zone record and a provider slot spent
106
+ * on a site that cannot use them.
107
+ */ export async function orgHoldsDedicatedSendingDomain(orgId) {
108
+ const id = String(orgId != null ? orgId : '').trim();
109
+ if (!id) return false;
110
+ const snapshot = await firestore().collection('orgs').doc(id).get().catch(()=>null);
111
+ return (snapshot == null ? void 0 : snapshot.exists) ? holdsDedicatedSendingDomain(snapshot.data()) : false;
112
+ }
113
+ /**
114
+ * The uniqueness index for pinned mail labels.
115
+ *
116
+ * A root collection with the LABEL as the document id, which is what makes the
117
+ * claim atomic: Firestore's `create` fails when the document exists, so two
118
+ * sites racing for one label produce one winner and one retry rather than two
119
+ * winners. This is the same technique the web-subdomain index uses, kept
120
+ * separate from it on purpose — see `platform-sending-domain.ts` for why the
121
+ * two namespaces do not share a claim.
122
+ */ export const SENDING_LABELS_COLLECTION = 'sendingLabels';
123
+ /** How many labels to try before giving up on a site's name. */ const MAX_LABEL_ATTEMPTS = 12;
124
+ const labelRef = (label)=>firestore().collection(SENDING_LABELS_COLLECTION).doc(label);
125
+ /**
126
+ * Claim the sending domain a site has ASKED for, or return the one it has.
127
+ *
128
+ * Idempotent in the way that matters for a two-vendor provisioning flow: it is
129
+ * the FIRST of the three writes, and it is the one that decides the name. Once
130
+ * it has returned a domain, every retry — of this function, of the Resend
131
+ * call, of the DNS write — resolves against the same name. A retry can
132
+ * therefore never produce a second Resend domain or a second record set,
133
+ * because there is no second name for them to be created under.
134
+ *
135
+ * The three writes it makes are ordered so that a crash between any two leaves
136
+ * a state the sweep can finish rather than one it must untangle:
137
+ *
138
+ * 1. Claim the label. Fails closed on a race; nothing else has happened yet.
139
+ * 2. Create the domain record at `requested`. This is what the console sweep
140
+ * looks for, so a crash after this point still gets picked up.
141
+ * 3. Point the host at it. A crash before this leaves an unreferenced record
142
+ * that the sweep will provision and the next call will attach.
143
+ *
144
+ * Step 3 is deliberately last and deliberately non-destructive: a host that
145
+ * already carries a `sendingLabel` returns early at the top and never reaches
146
+ * any of this.
147
+ */ export async function requestHostSendingDomain(options) {
148
+ var _ref, _ref1, _hostSnapshot_get;
149
+ const hostId = String((_ref = options == null ? void 0 : options.hostId) != null ? _ref : '').trim();
150
+ const orgId = String((_ref1 = options == null ? void 0 : options.orgId) != null ? _ref1 : '').trim();
151
+ // Narrowed rather than trusted: `strictNullChecks` is off, so the declared
152
+ // union does not survive a caller that reached this through `as never`, and
153
+ // an unrecognized value stored on the host would make the audit trail read
154
+ // as though somebody had asked when nothing did.
155
+ const requestedBy = (options == null ? void 0 : options.requestedBy) === 'staff' ? 'staff' : 'merchant';
156
+ if (!hostId || !orgId) {
157
+ return {
158
+ domain: null,
159
+ label: null,
160
+ created: false,
161
+ error: 'missing-host'
162
+ };
163
+ }
164
+ const hostRef = firestore().collection('hosts').doc(hostId);
165
+ const hostSnapshot = await hostRef.get();
166
+ if (!hostSnapshot.exists) {
167
+ return {
168
+ domain: null,
169
+ label: null,
170
+ created: false,
171
+ error: 'no-host'
172
+ };
173
+ }
174
+ /*
175
+ * ALREADY PINNED — the rename guarantee, and the idempotency guarantee, are
176
+ * the same early return.
177
+ *
178
+ * Read from the stored label rather than from the stored domain so the apex
179
+ * remains a configuration value: an operator who moves the mail namespace
180
+ * gets existing sites rebuilt onto the new apex under their existing labels,
181
+ * rather than sites pinned to an apex they have left.
182
+ */ const pinned = String((_hostSnapshot_get = hostSnapshot.get('sendingLabel')) != null ? _hostSnapshot_get : '').trim();
183
+ if (pinned) {
184
+ const domain = platformSendingDomainFor(pinned);
185
+ return {
186
+ domain: domain || null,
187
+ label: pinned,
188
+ created: false,
189
+ // A pinned label that no longer builds a domain means the apex changed
190
+ // to something the label cannot live under, or the label was written by
191
+ // hand. Naming it is the only useful thing to do — silently re-pinning
192
+ // would move a site's mail, which is the one thing this must not do.
193
+ error: domain ? null : 'pinned-label-unusable'
194
+ };
195
+ }
196
+ /*
197
+ * THE PLAN GATE, AT THE ONLY WRITER.
198
+ *
199
+ * Below the pinned-label early return on purpose: a site that already HAS a
200
+ * domain keeps it through a downgrade. Taking one away would move that site's
201
+ * mail onto a cold pooled identity and throw away whatever reputation the
202
+ * name had earned, which is a punishment the merchant did not agree to and
203
+ * which we would be inflicting silently. Reclaiming a downgraded site's
204
+ * domain is a teardown decision with its own surface, not a side effect of
205
+ * the next sweep.
206
+ *
207
+ * Here rather than only at the callers because this is the ONE function that
208
+ * creates a claim — the identity route's request action and
209
+ * `restartHostSendingDomain` both funnel through it. A gate at the call
210
+ * sites is a gate the next one skips, and the next one costs a zone record
211
+ * that nothing reclaims.
212
+ *
213
+ * It is the SECOND of two conditions and not the only one. `requestedBy`
214
+ * says a person asked; this says their plan carries what they asked for.
215
+ * Neither implies the other, and a claim needs both.
216
+ */ if (!await orgHoldsDedicatedSendingDomain(orgId)) {
217
+ return {
218
+ domain: null,
219
+ label: null,
220
+ created: false,
221
+ error: 'plan-no-dedicated-domain'
222
+ };
223
+ }
224
+ const subdomain = String((options == null ? void 0 : options.subdomain) || hostSnapshot.get('subdomain') || '').trim();
225
+ for(let attempt = 1; attempt <= MAX_LABEL_ATTEMPTS; attempt += 1){
226
+ const label = mailLabelCandidate(subdomain, attempt);
227
+ if (!label) continue;
228
+ const domain = platformSendingDomainFor(label);
229
+ if (!domain) continue;
230
+ const claim = {
231
+ label,
232
+ hostId,
233
+ orgId,
234
+ domain,
235
+ claimedAtMs: Date.now()
236
+ };
237
+ try {
238
+ // `create`, not `set`. An existing document is a claim by somebody else
239
+ // — possibly this same host on a previous run, which the early return
240
+ // above has already excluded — and overwriting it would hand two sites
241
+ // one mail domain.
242
+ await labelRef(label).create(claim);
243
+ } catch (unused) {
244
+ continue;
245
+ }
246
+ const record = {
247
+ domain,
248
+ status: 'requested',
249
+ // Proposed only. Resend signs on a selector of its own choosing and
250
+ // `recordIssuedSendingDomain` overwrites this with what it returns.
251
+ dkimSelector: 'aglyn',
252
+ dkimPublicKey: null,
253
+ returnPathHost: null,
254
+ createdAtMs: Date.now()
255
+ };
256
+ await firestore().collection('orgs').doc(orgId).collection(SENDING_DOMAINS_COLLECTION).doc(domain).set(record, {
257
+ merge: true
258
+ });
259
+ /*
260
+ * The label, the domain, and WHO ASKED FOR IT.
261
+ *
262
+ * The request stamp is written with the pointer rather than beside it, so
263
+ * a site can never hold a dedicated domain that no request accounts for.
264
+ * It is what lets an operator at the ceiling answer the only question that
265
+ * matters there — which of these slots somebody actually wanted — without
266
+ * reading a year of logs, and it is the audit trail for a support claim
267
+ * made on a merchant's behalf.
268
+ */ await hostRef.set({
269
+ sendingLabel: label,
270
+ sendingDomain: domain,
271
+ sendingDomainRequestedAtMs: Date.now(),
272
+ sendingDomainRequestedBy: requestedBy
273
+ }, {
274
+ merge: true
275
+ });
276
+ return {
277
+ domain,
278
+ label,
279
+ created: true,
280
+ error: null
281
+ };
282
+ }
283
+ return {
284
+ domain: null,
285
+ label: null,
286
+ created: false,
287
+ error: 'no-label'
288
+ };
289
+ }
290
+ /**
291
+ * What a host's deletion has to clean up, read before anything is deleted.
292
+ *
293
+ * Split from the deletion itself so the caller can do the vendor work FIRST
294
+ * and delete our records only once it has succeeded. The other order loses the
295
+ * name of the thing it still has to remove: a zone accumulating records for
296
+ * sites that no longer exist is bad on its own, and a future site reusing the
297
+ * label would inherit a stranger's DKIM key — which is a working signature for
298
+ * a domain somebody else's mail leaves on.
299
+ */ export async function readHostSendingTeardown(hostId) {
300
+ var _snapshot_get, _snapshot_get1, _ref;
301
+ const id = String(hostId != null ? hostId : '').trim();
302
+ if (!id) return null;
303
+ const snapshot = await firestore().collection('hosts').doc(id).get();
304
+ if (!snapshot.exists) return null;
305
+ const label = String((_snapshot_get = snapshot.get('sendingLabel')) != null ? _snapshot_get : '').trim() || null;
306
+ const domain = normalizeSendingDomain(String((_snapshot_get1 = snapshot.get('sendingDomain')) != null ? _snapshot_get1 : '')) || null;
307
+ if (!label && !domain) return null;
308
+ // The claim carries the org, so a host document that has lost its org
309
+ // pointer can still be torn down.
310
+ const claim = label ? await labelRef(label).get() : null;
311
+ const orgId = String((_ref = claim == null ? void 0 : claim.get('orgId')) != null ? _ref : '').trim() || null;
312
+ let providerDomainId = null;
313
+ let dkimSelector = null;
314
+ let trackingTarget = null;
315
+ if (orgId && domain) {
316
+ var _ref1, _ref2, _ref3;
317
+ const record = readSendingDomainRecord(await firestore().collection('orgs').doc(orgId).collection(SENDING_DOMAINS_COLLECTION).doc(domain).get());
318
+ providerDomainId = (_ref1 = record == null ? void 0 : record.providerDomainId) != null ? _ref1 : null;
319
+ dkimSelector = String((_ref2 = record == null ? void 0 : record.dkimSelector) != null ? _ref2 : '').trim() || null;
320
+ trackingTarget = String((_ref3 = record == null ? void 0 : record.trackingTarget) != null ? _ref3 : '').trim() || null;
321
+ }
322
+ return {
323
+ hostId: id,
324
+ orgId,
325
+ label,
326
+ domain,
327
+ providerDomainId,
328
+ dkimSelector,
329
+ trackingTarget
330
+ };
331
+ }
332
+ /**
333
+ * Drop our own record of a site's sending domain, once the vendors are clean.
334
+ *
335
+ * Releasing the label LAST is deliberate. The label is what makes the domain
336
+ * findable; while it exists, a half-finished teardown is a thing the sweep can
337
+ * find and finish. Released first, a crash mid-teardown would leave a Resend
338
+ * domain and a set of DNS records that nothing points at and nothing will ever
339
+ * look for again.
340
+ *
341
+ * The release is real rather than a tombstone. Once the DKIM record is gone
342
+ * from the zone and the domain object is gone from the account, a future site
343
+ * claiming the label inherits nothing — no key, no records, no reputation. A
344
+ * permanent reservation would only be needed if teardown were unreliable, and
345
+ * the answer to unreliable teardown is to fix it, not to leak a name for every
346
+ * site ever deleted.
347
+ */ export async function releaseHostSendingDomain(teardown) {
348
+ var _this;
349
+ if (!teardown) return;
350
+ if (teardown.orgId && teardown.domain) {
351
+ await firestore().collection('orgs').doc(teardown.orgId).collection(SENDING_DOMAINS_COLLECTION).doc(teardown.domain).delete().catch(()=>undefined);
352
+ }
353
+ /*
354
+ * Only onto a host that is still there. A merging `set` CREATES the document
355
+ * it is given, so clearing these two fields on a site that has already been
356
+ * erased writes an empty `hosts/{hostId}` back into existence — a fragment
357
+ * of a site the customer asked us to destroy, resurrected by its own
358
+ * cleanup, and one the routing sweep would then have to collect again.
359
+ */ const hostRef = firestore().collection('hosts').doc(teardown.hostId);
360
+ if ((_this = await hostRef.get().catch(()=>null)) == null ? void 0 : _this.exists) {
361
+ await hostRef.set({
362
+ sendingLabel: firebaseAdmin.firestore.FieldValue.delete(),
363
+ sendingDomain: firebaseAdmin.firestore.FieldValue.delete()
364
+ }, {
365
+ merge: true
366
+ }).catch(()=>undefined);
367
+ }
368
+ if (teardown.label) {
369
+ /*
370
+ * Only if it still names THIS host. A label released by a teardown that
371
+ * raced a re-provision would otherwise delete the claim the new domain
372
+ * depends on, and the next site to ask for that name would be handed one
373
+ * that is already live.
374
+ */ const ref = labelRef(teardown.label);
375
+ await firestore().runTransaction(async (transaction)=>{
376
+ var _snapshot_get;
377
+ const snapshot = await transaction.get(ref);
378
+ if (!snapshot.exists) return;
379
+ if (String((_snapshot_get = snapshot.get('hostId')) != null ? _snapshot_get : '') !== teardown.hostId) return;
380
+ transaction.delete(ref);
381
+ }).catch(()=>undefined);
382
+ }
383
+ }
384
+ /**
385
+ * Give a site a NEW sending domain, abandoning the one it has.
386
+ *
387
+ * The explicit action behind "I want my mail to come from a different name".
388
+ * It exists so that renaming a site can stay free of side effects: a merchant
389
+ * who wants the sending name to follow the rename asks for it here, and is
390
+ * told what it costs.
391
+ *
392
+ * What it costs is the reputation. A sending domain's value is its age and its
393
+ * history with the receiving networks; a new one starts at zero and is treated
394
+ * with more suspicion than the old one, for weeks. That is why this is not
395
+ * what a rename does, and why the caller is expected to have said so out loud
396
+ * before getting here.
397
+ *
398
+ * Returns the teardown for the OLD domain. The caller must complete the vendor
399
+ * cleanup for it, exactly as for a deleted host — the old Resend domain and
400
+ * its zone records are no more welcome to linger here than there.
401
+ */ export async function restartHostSendingDomain(options) {
402
+ const teardown = await readHostSendingTeardown(options == null ? void 0 : options.hostId);
403
+ if (teardown) await releaseHostSendingDomain(teardown);
404
+ return {
405
+ teardown,
406
+ provisioned: await requestHostSendingDomain(options)
407
+ };
408
+ }
409
+ /**
410
+ * Platform sending domains that still need vendor work, oldest first.
411
+ *
412
+ * A collection-group query over every org's `sendingDomains`, filtered to the
413
+ * ones inside our own mail apex — a customer's own domain is records they
414
+ * publish, and this sweep must never try to write to a zone we do not own.
415
+ *
416
+ * `orderBy` on the creation time, and not merely `limit`. A `limit()` with no
417
+ * `orderBy` returns documents in ID order, which is a stable arbitrary sample
418
+ * rather than a queue: the same handful of domains would be returned on every
419
+ * run and anything sorting after them would never be provisioned at all.
420
+ *
421
+ * Ordering on a field means a document missing that field is dropped by the
422
+ * query. That is acceptable here and nowhere else in this file, because
423
+ * `requestHostSendingDomain` is the only writer and it always sets
424
+ * `createdAtMs`.
425
+ */ export async function listPendingSendingDomains(limit = 25) {
426
+ const snapshot = await firestore().collectionGroup(SENDING_DOMAINS_COLLECTION).where('status', '==', 'requested').orderBy('createdAtMs', 'asc').limit(Math.max(1, limit)).get();
427
+ const pending = [];
428
+ for (const doc of snapshot.docs){
429
+ var _doc_ref_parent_parent;
430
+ const record = readSendingDomainRecord(doc);
431
+ if (!record) continue;
432
+ // Ours to provision, or the customer's to publish. Only the first kind.
433
+ if (!isPlatformSendingDomain(record.domain)) continue;
434
+ const orgId = (_doc_ref_parent_parent = doc.ref.parent.parent) == null ? void 0 : _doc_ref_parent_parent.id;
435
+ if (!orgId) continue;
436
+ pending.push({
437
+ orgId,
438
+ record
439
+ });
440
+ }
441
+ return pending;
442
+ }
443
+ /**
444
+ * The label a platform domain belongs to, for a caller assembling zone record
445
+ * names. Re-derived rather than read off the host, so a cleanup can name the
446
+ * records for a host document that is already gone.
447
+ */ export function sendingDomainLabel(domain) {
448
+ return platformSendingLabel(domain, platformSendingApex());
449
+ }
450
+
451
+ //# sourceMappingURL=host-sending-domain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/host-sending-domain.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 PER-SITE SENDING DOMAIN — the claim, and nothing else.\n *\n * Provisioning a site's sending domain is two writes to two vendors and one\n * write of our own. This module owns only the third: the LABEL CLAIM and the\n * record that says a domain has been asked for. It calls no vendor, reads no\n * credential, and is safe for the tenant runtime to import — which matters,\n * because the tenant runtime is where a site first discovers it needs to send.\n *\n * The vendor half lives in the console (`provision-sending-domain.ts`), for\n * the same structural reason `sending-domain-provider.ts` does: the keys that\n * create a Resend domain and write our DNS zone must not be reachable from a\n * process that serves published sites.\n *\n * ## Request here, issue there\n *\n * That split is what makes provisioning ON DEMAND possible at all.\n * {@link requestHostSendingDomain} is a cheap Firestore write with no\n * credential behind it; the console-side sweep picks the claim up and does the\n * vendor work, and the re-check sweep then moves the domain to `verified` on\n * its own.\n *\n * So a site is never blocked waiting on a credential it cannot hold, and the\n * credential is never held by a process that should not have it.\n *\n * ## NOTHING HERE RUNS WITHOUT SOMEBODY ASKING\n *\n * A dedicated subdomain is the one shape in the sending model that draws on a\n * resource we cannot buy our way out of at scale: a slot in the provider's\n * account-wide domain allowance, three records in our own zone, and a\n * permanent place in the re-verification sweep. The shared pool is flat at any\n * number of sites, and a domain the CUSTOMER owns costs our zone nothing at\n * all — so this is the only one that has to be rationed, and the only one a\n * ceiling can bind.\n *\n * It used to be claimed automatically at three moments: site creation, the\n * billing webhook's upgrade transition, and a sweep that walked hosts looking\n * for entitled sites without one. All three made the count grow with paying\n * customers rather than with anybody's decision, and a merchant who never\n * wanted an Aglyn-branded sending name still spent a slot on one.\n *\n * The function is named for a REQUEST rather than for an invariant on purpose.\n * `ensure…` is an invitation to call it defensively — \"just make sure it\n * exists\" — which is exactly how the three automatic callers came to be, and\n * `requestedBy` cannot be supplied by a caller that has nobody to name. A\n * caller that finds itself inventing a value for it is a caller that should\n * not be claiming.\n *\n * ## The label is pinned, and a rename does not move it\n *\n * `hosts/{hostId}.sendingLabel` is written ONCE and never rewritten. A site's\n * `subdomain` is mutable — the rename route rewrites that field in place — and\n * a sending domain derived from it would move every time a merchant renamed\n * their site, discarding the accumulated sending reputation that is the whole\n * point of having a domain per tenant, and stranding the return path that mail\n * already in flight will bounce to.\n *\n * The web address follows the slug. The sending identity does not. A merchant\n * who genuinely wants a different sending name gets\n * {@link restartHostSendingDomain}, which says plainly that it starts\n * reputation over — never a silent side effect of renaming.\n *\n * The label is proposed from the site's subdomain AT THE MOMENT OF THE\n * REQUEST, which is the name the merchant is looking at when they ask. It was\n * previously the creation-time slug, because the claim happened at creation\n * and there was no later moment to read; a name derived from what the site is\n * called today is the less surprising of the two, and the pin is what makes it\n * stable from then on either way.\n */\n\nimport {\n isPlatformSendingDomain,\n mailLabelCandidate,\n normalizeSendingDomain,\n platformSendingApex,\n platformSendingDomainFor,\n platformSendingLabel,\n type SendingDomainRecord,\n} from '@aglyn/shared-util-email'\nimport { holdsDedicatedSendingDomain } from '@aglyn/aglyn/app-utils/dedicated-sending-domain'\nimport firebaseAdmin from './firebase-admin'\nimport { SENDING_DOMAINS_COLLECTION, readSendingDomainRecord } from './sending-domains'\n\nconst firestore = () => firebaseAdmin.app().firestore()\n\n/**\n * Whether this org's sites may hold a dedicated domain.\n *\n * One read, and only ever on a CLAIM path — never on a send. A site that does\n * not qualify has a sending identity regardless (the shared pool), so nothing\n * here is on the critical path of a message.\n *\n * The WHOLE org document goes to the gate rather than its `plan` field, and\n * that is what makes a per-org grant real: the entitlement resolves the plan's\n * default and then the org's own overrides, so an account staff have granted\n * one may claim without being moved up a tier — and an account whose\n * subscription has died may not, because `resolveEffectivePlan` reads a dead\n * subscription down to `free`. A single field would have hidden both.\n *\n * Fails closed on a read error, and that is the safe direction: the cost of\n * answering `false` wrongly is a request the merchant makes again, while the\n * cost of answering `true` wrongly is a zone record and a provider slot spent\n * on a site that cannot use them.\n */\nexport async function orgHoldsDedicatedSendingDomain(\n orgId: string | null | undefined,\n): Promise<boolean> {\n const id = String(orgId ?? '').trim()\n if (!id) return false\n const snapshot = await firestore()\n .collection('orgs')\n .doc(id)\n .get()\n .catch(() => null)\n return snapshot?.exists\n ? holdsDedicatedSendingDomain(snapshot.data() as never)\n : false\n}\n\n/**\n * The uniqueness index for pinned mail labels.\n *\n * A root collection with the LABEL as the document id, which is what makes the\n * claim atomic: Firestore's `create` fails when the document exists, so two\n * sites racing for one label produce one winner and one retry rather than two\n * winners. This is the same technique the web-subdomain index uses, kept\n * separate from it on purpose — see `platform-sending-domain.ts` for why the\n * two namespaces do not share a claim.\n */\nexport const SENDING_LABELS_COLLECTION = 'sendingLabels'\n\n/** How many labels to try before giving up on a site's name. */\nconst MAX_LABEL_ATTEMPTS = 12\n\nconst labelRef = (label: string) =>\n firestore().collection(SENDING_LABELS_COLLECTION).doc(label)\n\nexport interface SendingLabelClaim {\n label: string\n hostId: string\n orgId: string\n domain: string\n claimedAtMs: number\n}\n\nexport interface HostSendingDomainResult {\n /** The site's sending domain, or null when none could be provisioned. */\n domain: string | null\n label: string | null\n /** True when this call created the claim rather than finding one. */\n created: boolean\n /** Why nothing could be claimed. Null on success. */\n error: string | null\n}\n\n/**\n * Claim the sending domain a site has ASKED for, or return the one it has.\n *\n * Idempotent in the way that matters for a two-vendor provisioning flow: it is\n * the FIRST of the three writes, and it is the one that decides the name. Once\n * it has returned a domain, every retry — of this function, of the Resend\n * call, of the DNS write — resolves against the same name. A retry can\n * therefore never produce a second Resend domain or a second record set,\n * because there is no second name for them to be created under.\n *\n * The three writes it makes are ordered so that a crash between any two leaves\n * a state the sweep can finish rather than one it must untangle:\n *\n * 1. Claim the label. Fails closed on a race; nothing else has happened yet.\n * 2. Create the domain record at `requested`. This is what the console sweep\n * looks for, so a crash after this point still gets picked up.\n * 3. Point the host at it. A crash before this leaves an unreferenced record\n * that the sweep will provision and the next call will attach.\n *\n * Step 3 is deliberately last and deliberately non-destructive: a host that\n * already carries a `sendingLabel` returns early at the top and never reaches\n * any of this.\n */\nexport async function requestHostSendingDomain(options: {\n hostId: string\n orgId: string\n /** The site's CURRENT subdomain. Read once, to propose a label. */\n subdomain: string\n /**\n * WHO ASKED. Required, and there is no default.\n *\n * A dedicated subdomain is a draw on a bounded resource, so the record of\n * who drew it is part of the claim rather than something reconstructed from\n * logs later. `merchant` is somebody with `org.settings` acting on their own\n * workspace; `staff` is a support action taken on their behalf.\n *\n * Its real work is at compile time. A caller that wants to claim\n * defensively — on a plan change, on a sweep, on site creation — has no\n * honest value to put here, and that is the point: the parameter cannot be\n * satisfied by a process, only by a person.\n */\n requestedBy: 'merchant' | 'staff'\n}): Promise<HostSendingDomainResult> {\n const hostId = String(options?.hostId ?? '').trim()\n const orgId = String(options?.orgId ?? '').trim()\n // Narrowed rather than trusted: `strictNullChecks` is off, so the declared\n // union does not survive a caller that reached this through `as never`, and\n // an unrecognized value stored on the host would make the audit trail read\n // as though somebody had asked when nothing did.\n const requestedBy = options?.requestedBy === 'staff' ? 'staff' : 'merchant'\n if (!hostId || !orgId) {\n return { domain: null, label: null, created: false, error: 'missing-host' }\n }\n\n const hostRef = firestore().collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) {\n return { domain: null, label: null, created: false, error: 'no-host' }\n }\n\n /*\n * ALREADY PINNED — the rename guarantee, and the idempotency guarantee, are\n * the same early return.\n *\n * Read from the stored label rather than from the stored domain so the apex\n * remains a configuration value: an operator who moves the mail namespace\n * gets existing sites rebuilt onto the new apex under their existing labels,\n * rather than sites pinned to an apex they have left.\n */\n const pinned = String(hostSnapshot.get('sendingLabel') ?? '').trim()\n if (pinned) {\n const domain = platformSendingDomainFor(pinned)\n return {\n domain: domain || null,\n label: pinned,\n created: false,\n // A pinned label that no longer builds a domain means the apex changed\n // to something the label cannot live under, or the label was written by\n // hand. Naming it is the only useful thing to do — silently re-pinning\n // would move a site's mail, which is the one thing this must not do.\n error: domain ? null : 'pinned-label-unusable',\n }\n }\n\n /*\n * THE PLAN GATE, AT THE ONLY WRITER.\n *\n * Below the pinned-label early return on purpose: a site that already HAS a\n * domain keeps it through a downgrade. Taking one away would move that site's\n * mail onto a cold pooled identity and throw away whatever reputation the\n * name had earned, which is a punishment the merchant did not agree to and\n * which we would be inflicting silently. Reclaiming a downgraded site's\n * domain is a teardown decision with its own surface, not a side effect of\n * the next sweep.\n *\n * Here rather than only at the callers because this is the ONE function that\n * creates a claim — the identity route's request action and\n * `restartHostSendingDomain` both funnel through it. A gate at the call\n * sites is a gate the next one skips, and the next one costs a zone record\n * that nothing reclaims.\n *\n * It is the SECOND of two conditions and not the only one. `requestedBy`\n * says a person asked; this says their plan carries what they asked for.\n * Neither implies the other, and a claim needs both.\n */\n if (!(await orgHoldsDedicatedSendingDomain(orgId))) {\n return {\n domain: null,\n label: null,\n created: false,\n error: 'plan-no-dedicated-domain',\n }\n }\n\n const subdomain = String(\n options?.subdomain || hostSnapshot.get('subdomain') || '',\n ).trim()\n\n for (let attempt = 1; attempt <= MAX_LABEL_ATTEMPTS; attempt += 1) {\n const label = mailLabelCandidate(subdomain, attempt)\n if (!label) continue\n const domain = platformSendingDomainFor(label)\n if (!domain) continue\n\n const claim: SendingLabelClaim = {\n label,\n hostId,\n orgId,\n domain,\n claimedAtMs: Date.now(),\n }\n\n try {\n // `create`, not `set`. An existing document is a claim by somebody else\n // — possibly this same host on a previous run, which the early return\n // above has already excluded — and overwriting it would hand two sites\n // one mail domain.\n await labelRef(label).create(claim)\n } catch {\n continue\n }\n\n const record: SendingDomainRecord = {\n domain,\n status: 'requested',\n // Proposed only. Resend signs on a selector of its own choosing and\n // `recordIssuedSendingDomain` overwrites this with what it returns.\n dkimSelector: 'aglyn',\n dkimPublicKey: null,\n returnPathHost: null,\n createdAtMs: Date.now(),\n }\n await firestore()\n .collection('orgs')\n .doc(orgId)\n .collection(SENDING_DOMAINS_COLLECTION)\n .doc(domain)\n .set(record, { merge: true })\n\n /*\n * The label, the domain, and WHO ASKED FOR IT.\n *\n * The request stamp is written with the pointer rather than beside it, so\n * a site can never hold a dedicated domain that no request accounts for.\n * It is what lets an operator at the ceiling answer the only question that\n * matters there — which of these slots somebody actually wanted — without\n * reading a year of logs, and it is the audit trail for a support claim\n * made on a merchant's behalf.\n */\n await hostRef.set(\n {\n sendingLabel: label,\n sendingDomain: domain,\n sendingDomainRequestedAtMs: Date.now(),\n sendingDomainRequestedBy: requestedBy,\n },\n { merge: true },\n )\n\n return { domain, label, created: true, error: null }\n }\n\n return { domain: null, label: null, created: false, error: 'no-label' }\n}\n\n/**\n * Everything provisioned for one site, for a caller that must take it apart.\n *\n * Returned as data rather than acted on, because the acting needs vendor\n * credentials this module must not hold. The console's cleanup path takes this\n * and makes the calls.\n */\nexport interface HostSendingDomainTeardown {\n hostId: string\n orgId: string | null\n label: string | null\n domain: string | null\n /** The provider's id for the domain object, when one was recorded. */\n providerDomainId: string | null\n /**\n * The selector the provider signs under, needed to name the DKIM record for\n * deletion. Read from the record rather than assumed: the provider chooses\n * it, and a guessed selector deletes nothing while reporting success — which\n * would leave a live signing key in the zone for a site that no longer\n * exists, ready to be inherited by whoever claims the label next.\n */\n dkimSelector: string | null\n /**\n * The provider's tracking host, when this domain had one.\n *\n * Read because it changes WHEN the domain may be released, not just what is\n * deleted: links in mail this domain has already sent point at that host,\n * and deleting the domain object takes it with them. See the retention\n * window in `teardownSendingDomain`.\n */\n trackingTarget: string | null\n}\n\n/**\n * What a host's deletion has to clean up, read before anything is deleted.\n *\n * Split from the deletion itself so the caller can do the vendor work FIRST\n * and delete our records only once it has succeeded. The other order loses the\n * name of the thing it still has to remove: a zone accumulating records for\n * sites that no longer exist is bad on its own, and a future site reusing the\n * label would inherit a stranger's DKIM key — which is a working signature for\n * a domain somebody else's mail leaves on.\n */\nexport async function readHostSendingTeardown(\n hostId: string,\n): Promise<HostSendingDomainTeardown | null> {\n const id = String(hostId ?? '').trim()\n if (!id) return null\n\n const snapshot = await firestore().collection('hosts').doc(id).get()\n if (!snapshot.exists) return null\n\n const label = String(snapshot.get('sendingLabel') ?? '').trim() || null\n const domain =\n normalizeSendingDomain(String(snapshot.get('sendingDomain') ?? '')) || null\n if (!label && !domain) return null\n\n // The claim carries the org, so a host document that has lost its org\n // pointer can still be torn down.\n const claim = label ? await labelRef(label).get() : null\n const orgId = String(claim?.get('orgId') ?? '').trim() || null\n\n let providerDomainId: string | null = null\n let dkimSelector: string | null = null\n let trackingTarget: string | null = null\n if (orgId && domain) {\n const record = readSendingDomainRecord(\n await firestore()\n .collection('orgs')\n .doc(orgId)\n .collection(SENDING_DOMAINS_COLLECTION)\n .doc(domain)\n .get(),\n )\n providerDomainId = record?.providerDomainId ?? null\n dkimSelector = String(record?.dkimSelector ?? '').trim() || null\n trackingTarget = String(record?.trackingTarget ?? '').trim() || null\n }\n\n return {\n hostId: id,\n orgId,\n label,\n domain,\n providerDomainId,\n dkimSelector,\n trackingTarget,\n }\n}\n\n/**\n * Drop our own record of a site's sending domain, once the vendors are clean.\n *\n * Releasing the label LAST is deliberate. The label is what makes the domain\n * findable; while it exists, a half-finished teardown is a thing the sweep can\n * find and finish. Released first, a crash mid-teardown would leave a Resend\n * domain and a set of DNS records that nothing points at and nothing will ever\n * look for again.\n *\n * The release is real rather than a tombstone. Once the DKIM record is gone\n * from the zone and the domain object is gone from the account, a future site\n * claiming the label inherits nothing — no key, no records, no reputation. A\n * permanent reservation would only be needed if teardown were unreliable, and\n * the answer to unreliable teardown is to fix it, not to leak a name for every\n * site ever deleted.\n */\nexport async function releaseHostSendingDomain(\n teardown: HostSendingDomainTeardown,\n): Promise<void> {\n if (!teardown) return\n\n if (teardown.orgId && teardown.domain) {\n await firestore()\n .collection('orgs')\n .doc(teardown.orgId)\n .collection(SENDING_DOMAINS_COLLECTION)\n .doc(teardown.domain)\n .delete()\n .catch(() => undefined)\n }\n\n /*\n * Only onto a host that is still there. A merging `set` CREATES the document\n * it is given, so clearing these two fields on a site that has already been\n * erased writes an empty `hosts/{hostId}` back into existence — a fragment\n * of a site the customer asked us to destroy, resurrected by its own\n * cleanup, and one the routing sweep would then have to collect again.\n */\n const hostRef = firestore().collection('hosts').doc(teardown.hostId)\n if ((await hostRef.get().catch(() => null))?.exists) {\n await hostRef\n .set(\n {\n sendingLabel: firebaseAdmin.firestore.FieldValue.delete(),\n sendingDomain: firebaseAdmin.firestore.FieldValue.delete(),\n },\n { merge: true },\n )\n .catch(() => undefined)\n }\n\n if (teardown.label) {\n /*\n * Only if it still names THIS host. A label released by a teardown that\n * raced a re-provision would otherwise delete the claim the new domain\n * depends on, and the next site to ask for that name would be handed one\n * that is already live.\n */\n const ref = labelRef(teardown.label)\n await firestore()\n .runTransaction(async (transaction) => {\n const snapshot = await transaction.get(ref)\n if (!snapshot.exists) return\n if (String(snapshot.get('hostId') ?? '') !== teardown.hostId) return\n transaction.delete(ref)\n })\n .catch(() => undefined)\n }\n}\n\n/**\n * Give a site a NEW sending domain, abandoning the one it has.\n *\n * The explicit action behind \"I want my mail to come from a different name\".\n * It exists so that renaming a site can stay free of side effects: a merchant\n * who wants the sending name to follow the rename asks for it here, and is\n * told what it costs.\n *\n * What it costs is the reputation. A sending domain's value is its age and its\n * history with the receiving networks; a new one starts at zero and is treated\n * with more suspicion than the old one, for weeks. That is why this is not\n * what a rename does, and why the caller is expected to have said so out loud\n * before getting here.\n *\n * Returns the teardown for the OLD domain. The caller must complete the vendor\n * cleanup for it, exactly as for a deleted host — the old Resend domain and\n * its zone records are no more welcome to linger here than there.\n */\nexport async function restartHostSendingDomain(options: {\n hostId: string\n orgId: string\n subdomain: string\n /** Carried through to the new claim. See `requestHostSendingDomain`. */\n requestedBy: 'merchant' | 'staff'\n}): Promise<{\n teardown: HostSendingDomainTeardown | null\n provisioned: HostSendingDomainResult\n}> {\n const teardown = await readHostSendingTeardown(options?.hostId)\n if (teardown) await releaseHostSendingDomain(teardown)\n return { teardown, provisioned: await requestHostSendingDomain(options) }\n}\n\n/*==========================================\n What the console sweep picks up\n==========================================*/\n\nexport interface PendingSendingDomain {\n orgId: string\n record: SendingDomainRecord\n}\n\n/**\n * Platform sending domains that still need vendor work, oldest first.\n *\n * A collection-group query over every org's `sendingDomains`, filtered to the\n * ones inside our own mail apex — a customer's own domain is records they\n * publish, and this sweep must never try to write to a zone we do not own.\n *\n * `orderBy` on the creation time, and not merely `limit`. A `limit()` with no\n * `orderBy` returns documents in ID order, which is a stable arbitrary sample\n * rather than a queue: the same handful of domains would be returned on every\n * run and anything sorting after them would never be provisioned at all.\n *\n * Ordering on a field means a document missing that field is dropped by the\n * query. That is acceptable here and nowhere else in this file, because\n * `requestHostSendingDomain` is the only writer and it always sets\n * `createdAtMs`.\n */\nexport async function listPendingSendingDomains(\n limit = 25,\n): Promise<PendingSendingDomain[]> {\n const snapshot = await firestore()\n .collectionGroup(SENDING_DOMAINS_COLLECTION)\n .where('status', '==', 'requested')\n .orderBy('createdAtMs', 'asc')\n .limit(Math.max(1, limit))\n .get()\n\n const pending: PendingSendingDomain[] = []\n for (const doc of snapshot.docs) {\n const record = readSendingDomainRecord(doc)\n if (!record) continue\n // Ours to provision, or the customer's to publish. Only the first kind.\n if (!isPlatformSendingDomain(record.domain)) continue\n const orgId = doc.ref.parent.parent?.id\n if (!orgId) continue\n pending.push({ orgId, record })\n }\n return pending\n}\n\n/**\n * The label a platform domain belongs to, for a caller assembling zone record\n * names. Re-derived rather than read off the host, so a cleanup can name the\n * records for a host document that is already gone.\n */\nexport function sendingDomainLabel(domain: string): string {\n return platformSendingLabel(domain, platformSendingApex())\n}\n\n"],"names":["isPlatformSendingDomain","mailLabelCandidate","normalizeSendingDomain","platformSendingApex","platformSendingDomainFor","platformSendingLabel","holdsDedicatedSendingDomain","firebaseAdmin","SENDING_DOMAINS_COLLECTION","readSendingDomainRecord","firestore","app","orgHoldsDedicatedSendingDomain","orgId","id","String","trim","snapshot","collection","doc","get","catch","exists","data","SENDING_LABELS_COLLECTION","MAX_LABEL_ATTEMPTS","labelRef","label","requestHostSendingDomain","options","hostSnapshot","hostId","requestedBy","domain","created","error","hostRef","pinned","subdomain","attempt","claim","claimedAtMs","Date","now","create","record","status","dkimSelector","dkimPublicKey","returnPathHost","createdAtMs","set","merge","sendingLabel","sendingDomain","sendingDomainRequestedAtMs","sendingDomainRequestedBy","readHostSendingTeardown","providerDomainId","trackingTarget","releaseHostSendingDomain","teardown","delete","undefined","FieldValue","ref","runTransaction","transaction","restartHostSendingDomain","provisioned","listPendingSendingDomains","limit","collectionGroup","where","orderBy","Math","max","pending","docs","parent","push","sendingDomainLabel"],"mappings":"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"}