@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,779 @@
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
+ * Registration, DNS ownership proof and Vercel attachment for a custom
19
+ * **console** domain — the "verify + attach" half of AGL-1099 (AGL-1373).
20
+ *
21
+ * ⚠️ **THIS LANDS DARK, AND MUST STAY DARK.** AGL-1099 says it outright:
22
+ * *"Do NOT ship 1099b alone: attaching a domain that then cannot authenticate
23
+ * is worse than not attaching it, because it looks finished."* Nothing here is
24
+ * reachable from a user-facing path, nothing calls `activateConsoleDomain`, and
25
+ * no domain has been attached. One thing must still land first:
26
+ *
27
+ * - **1099e — the handoff itself.** A console domain shares no parent domain
28
+ * with `aglyn.com`, so the `.aglyn.com` session cookie cannot reach it.
29
+ *
30
+ * ## 1099d is no longer one of them (AGL-1378, closed 2026-08-23)
31
+ *
32
+ * The 1099a proof-of-concept (`docs/design/agl-1099a-poc-findings.md`)
33
+ * measured that App Check, *not* Firebase's authorized-domain list, is the
34
+ * gate: an unattested `signInWithCustomToken` 401s with "Firebase App Check
35
+ * token is invalid" **before** token validation. Same page, same app id, only
36
+ * the origin differing — an allowlisted origin minted a token and reached
37
+ * credential validation; a non-allowlisted one failed at
38
+ * `appCheck/recaptcha-error`. That finding stands, and it is why the allowlist
39
+ * entry is a hard **functional** prerequisite rather than the commercial
40
+ * ceiling the design called it.
41
+ *
42
+ * What was wrong was the conclusion that the entry could only be added by
43
+ * hand. That rested on probing `aglyn-main`, which is not the project the key
44
+ * lives in; the key was auto-migrated into `recaptcha-migrated-…`, where the
45
+ * Enterprise API is enabled, we hold owner, and `projects.keys.patch` on
46
+ * `webSettings.allowedDomains` answers 200. `activateConsoleDomain` now writes
47
+ * the serving name through `recaptcha-allowlist.ts` and refuses to mark the
48
+ * claim `active` if that write does not land.
49
+ *
50
+ * ## Why this is not `/api/domains/attach|verify|detach`
51
+ *
52
+ * Those routes are shaped around `hosts/{hostId}`, a `cname` field and the
53
+ * `customDomain` entitlement. A console domain is keyed on the **domain**,
54
+ * owned by an **org**, and gated on `whiteLabel`. Re-pointing them at the
55
+ * console project would mean parameterising the auth model, the entitlement,
56
+ * the uniqueness query and the Firestore shape — at which point it is not
57
+ * reuse. What *is* reused is `workspace-domains.ts`, which already talks to
58
+ * `VERCEL_CONSOLE_PROJECT_ID`, already carries a deadline, and already never
59
+ * throws (AGL-1353 D8).
60
+ *
61
+ * ## The uniqueness invariant, which is the whole security property
62
+ *
63
+ * `attachProjectDomain` tolerates Vercel's `domain_already_in_use` as success.
64
+ * That is only safe while the Firestore claim indexes **every** name Vercel
65
+ * holds for us. If a name reaches Vercel without a claim, a second org can
66
+ * claim it here, read `already-exists` as health, and have its visitors served
67
+ * — or redirected — to a stranger's console. That is AGL-743 exactly: there,
68
+ * `host.cname` was written client-side *before* the uniqueness check, so losing
69
+ * the race still left the loser holding the domain and `get-host.ts` resolved
70
+ * the duplicate by Firestore document order.
71
+ *
72
+ * So: **`claimConsoleDomain` claims the primary and every twin in ONE
73
+ * transaction, or none of them**, and `activateConsoleDomain` attaches exactly
74
+ * the set that transaction claimed. Neither half is optional, and a twin
75
+ * introduced later without going through `consoleDomainNames` reopens the hole.
76
+ */ import { randomBytes } from "crypto";
77
+ import { checkEntitlement } from "@aglyn/aglyn/server";
78
+ import firebaseAdmin from "./firebase-admin.js";
79
+ import { challengeValue, recordsProveOwnership, resolveChallengeTxt, SSO_CHALLENGE_PREFIX } from "./sso-provisioning.js";
80
+ import { allowConsoleOrigin, allowlistSatisfied, reclaimConsoleOrigin } from "./recaptcha-allowlist.js";
81
+ import { attachProjectDomain, detachProjectDomain, pendingUploadCorsRemedy } from "./workspace-domains.js";
82
+ // The blocklist and the shape check, shared with the site custom-domain path
83
+ // so a name added to one is added to both (AGL-1430).
84
+ import { normalizePlatformDomain, validatePlatformDomain } from "./platform-domain-names.js";
85
+ const firestore = ()=>firebaseAdmin.app().firestore();
86
+ /** Collection name, kept in one place so the rules file and this agree. */ export const CONSOLE_DOMAINS_COLLECTION = 'consoleDomains';
87
+ /**
88
+ * Trim, lowercase, drop a trailing root dot, and tolerate a pasted URL.
89
+ *
90
+ * The console-flavoured name for the shared normaliser, kept because callers
91
+ * and the docs use it.
92
+ */ export const normalizeConsoleDomain = normalizePlatformDomain;
93
+ /**
94
+ * Shape, length and reserved-name check.
95
+ *
96
+ * The RULES moved to `platform-domain-names.ts` (AGL-1430) so the site
97
+ * custom-domain path could stop being the only surface without them — it was
98
+ * the one with the live hole. Only the wording stays here: these two strings
99
+ * are what a customer reads in the console-domain wizard.
100
+ *
101
+ * `{ domain, error }` with both keys always present rather than a discriminated
102
+ * union, matching `validateSsoDomain` — `strictNullChecks` is off repo-wide and
103
+ * an `{ ok: true } | { ok: false }` union does not narrow reliably once it
104
+ * crosses a library boundary.
105
+ */ export function validateConsoleDomain(input) {
106
+ return validatePlatformDomain(input, {
107
+ invalid: 'Enter a valid domain, for example console.acme.com',
108
+ reserved: 'That domain is reserved and cannot be used as a console domain'
109
+ });
110
+ }
111
+ /**
112
+ * Every name this claim covers: the primary, then its twins.
113
+ *
114
+ * **A twin is a name we attach to Vercel that is not the one the customer
115
+ * asked for**, and it is the sharp edge of the whole file. Attaching
116
+ * `acme.com` without also attaching `www.acme.com` leaves half the traffic on
117
+ * the floor; attaching `www.acme.com` without *claiming* it leaves a name in
118
+ * our Vercel project that no Firestore document accounts for — and then a
119
+ * second org claims it, `attachProjectDomain` answers `already-exists`, and
120
+ * that org's visitors are redirected to the first org's console. The name is
121
+ * therefore derived here, claimed here, and attached from the same list.
122
+ *
123
+ * The apex test is deliberately narrow: exactly two labels. `acme.co.uk` is
124
+ * also an apex and gets no twin, which costs a customer a `www` redirect they
125
+ * can ask for by hand. Guessing wrong in the other direction — treating
126
+ * `console.acme.com` as an apex and quietly reserving `www.console.acme.com` —
127
+ * would reserve names on an org's behalf that it never asked for, and a
128
+ * reservation is the thing that locks another org out.
129
+ */ export function consoleDomainNames(domain) {
130
+ const primary = normalizeConsoleDomain(domain);
131
+ if (!primary) return [];
132
+ const labels = primary.split('.');
133
+ if (labels.length !== 2) return [
134
+ primary
135
+ ];
136
+ return [
137
+ primary,
138
+ `www.${primary}`
139
+ ];
140
+ }
141
+ export class ConsoleDomainTakenError extends Error {
142
+ constructor(domain){
143
+ super(`Console domain already claimed: ${domain}`);
144
+ this.name = 'ConsoleDomainTakenError';
145
+ this.domain = domain;
146
+ }
147
+ }
148
+ const claimRef = (domain)=>firestore().collection(CONSOLE_DOMAINS_COLLECTION).doc(domain);
149
+ function claimView(domain, data) {
150
+ var _ref, _ref1, _ref2, _ref3;
151
+ const token = String((_ref = data == null ? void 0 : data.txtToken) != null ? _ref : '');
152
+ const names = Array.isArray(data == null ? void 0 : data.names) ? data.names : consoleDomainNames(domain);
153
+ return {
154
+ domain,
155
+ orgId: String((_ref1 = data == null ? void 0 : data.orgId) != null ? _ref1 : ''),
156
+ status: (_ref2 = data == null ? void 0 : data.status) != null ? _ref2 : 'pending',
157
+ token,
158
+ names,
159
+ recordHost: `${SSO_CHALLENGE_PREFIX}.${domain}`,
160
+ recordValue: challengeValue(token),
161
+ verified: (data == null ? void 0 : data.status) === 'verified' || (data == null ? void 0 : data.status) === 'active',
162
+ lastRecords: (_ref3 = data == null ? void 0 : data.lastRecords) != null ? _ref3 : undefined
163
+ };
164
+ }
165
+ /**
166
+ * The org's `whiteLabel` entitlement, read server-side.
167
+ *
168
+ * AGL-1354 removed `brandingProfile` from what a client may write, so a server
169
+ * route is now the only writer of `customConsoleDomain` — which makes this the
170
+ * only place the gate can live. A gate that is bypassable is not a gate, and
171
+ * this one was, on any plan, straight from the client SDK, until that landed.
172
+ */ async function orgIsEntitled(orgId) {
173
+ const snapshot = await firestore().collection('orgs').doc(orgId).get();
174
+ if (!snapshot.exists) return false;
175
+ return checkEntitlement(snapshot.data(), 'whiteLabel');
176
+ }
177
+ /**
178
+ * Claim the primary and every twin in ONE transaction, or none of them.
179
+ *
180
+ * Modelled on the `orgSlugs` reservation in `organizations.ts`: read inside the
181
+ * transaction, refuse if someone else holds it, write in the same transaction.
182
+ * Every name is read with a single `getAll` so the whole set is covered by one
183
+ * transaction's read-set — claiming a twin in a follow-up write is precisely
184
+ * the AGL-743 hole this exists to close.
185
+ *
186
+ * Idempotent for the owning org, and it **keeps the existing token**: reissuing
187
+ * would invalidate a TXT record the customer may already have published and
188
+ * turn a working setup into a mysterious failure (the same reasoning as
189
+ * `issueDomainClaim`).
190
+ *
191
+ * Throws `ConsoleDomainTakenError` when any name in the set belongs to another
192
+ * org. Exported for the integration test, which needs to drive the transaction
193
+ * under real contention.
194
+ */ export async function claimConsoleDomain(orgId, domain) {
195
+ const names = consoleDomainNames(domain);
196
+ const primary = names[0];
197
+ const db = firestore();
198
+ const now = firebaseAdmin.firestore.FieldValue.serverTimestamp();
199
+ return db.runTransaction(async (tx)=>{
200
+ var _existing_get, _existing_get1;
201
+ const refs = names.map((name)=>db.collection(CONSOLE_DOMAINS_COLLECTION).doc(name));
202
+ // ONE read covering the whole set. Firestore requires every read before
203
+ // every write in a transaction, so this also fixes the ordering.
204
+ const snapshots = await tx.getAll(...refs);
205
+ for (const snapshot of snapshots){
206
+ if (snapshot.exists && snapshot.get('orgId') !== orgId) {
207
+ throw new ConsoleDomainTakenError(snapshot.id);
208
+ }
209
+ }
210
+ const existing = snapshots[0];
211
+ const token = existing.exists ? String((_existing_get = existing.get('txtToken')) != null ? _existing_get : '') : randomBytes(24).toString('hex');
212
+ const status = existing.exists ? (_existing_get1 = existing.get('status')) != null ? _existing_get1 : 'pending' : 'pending';
213
+ snapshots.forEach((snapshot, index)=>{
214
+ var _snapshot_get;
215
+ tx.set(refs[index], {
216
+ orgId,
217
+ primaryHost: primary,
218
+ role: index === 0 ? 'primary' : 'redirect',
219
+ names,
220
+ txtToken: token,
221
+ status,
222
+ vercelState: (_snapshot_get = snapshot.get('vercelState')) != null ? _snapshot_get : 'pending',
223
+ createdAt: snapshot.exists ? snapshot.get('createdAt') : now,
224
+ updatedAt: now
225
+ }, {
226
+ merge: true
227
+ });
228
+ });
229
+ return claimView(primary, {
230
+ orgId,
231
+ status,
232
+ txtToken: token,
233
+ names
234
+ });
235
+ });
236
+ }
237
+ /**
238
+ * Start (or re-read) a registration: entitlement, validation, then the claim.
239
+ *
240
+ * The order matters. An org without `whiteLabel` must not be able to learn
241
+ * whether a domain is free, and must not leave a `pending` claim behind that
242
+ * blocks the org that *is* entitled to it.
243
+ */ export async function registerConsoleDomain(options) {
244
+ var _ref;
245
+ const orgId = String((_ref = options == null ? void 0 : options.orgId) != null ? _ref : '');
246
+ if (!orgId) return {
247
+ claim: null,
248
+ error: 'Unknown organization',
249
+ status: 400
250
+ };
251
+ if (!await orgIsEntitled(orgId)) {
252
+ return {
253
+ claim: null,
254
+ error: 'A custom console domain requires the Agency plan',
255
+ status: 403
256
+ };
257
+ }
258
+ const { domain, error } = validateConsoleDomain(options == null ? void 0 : options.domain);
259
+ if (!domain) return {
260
+ claim: null,
261
+ error: error != null ? error : 'Invalid domain',
262
+ status: 400
263
+ };
264
+ try {
265
+ return {
266
+ claim: await claimConsoleDomain(orgId, domain),
267
+ error: null,
268
+ status: 200
269
+ };
270
+ } catch (thrown) {
271
+ if (thrown instanceof ConsoleDomainTakenError) {
272
+ return {
273
+ claim: null,
274
+ error: 'That domain is already connected to another organization',
275
+ status: 409
276
+ };
277
+ }
278
+ throw thrown;
279
+ }
280
+ }
281
+ /**
282
+ * Re-read DNS and record the verdict.
283
+ *
284
+ * Re-reads the claim first and refuses if the caller does not own it — the
285
+ * `publishSsoDomains` discipline, where nothing reaches the collection sign-in
286
+ * consults until a fresh lookup has seen the record. The comparison itself is
287
+ * `recordsProveOwnership`, an exact match on a whole TXT record: a substring
288
+ * test would accept `aglyn-domain-verification=<token>-and-more`, and a prefix
289
+ * test would accept anything that merely starts the same way.
290
+ */ export async function verifyConsoleDomain(options) {
291
+ var _ref, _snapshot_get, _snapshot_get1;
292
+ const orgId = String((_ref = options == null ? void 0 : options.orgId) != null ? _ref : '');
293
+ const { domain, error } = validateConsoleDomain(options == null ? void 0 : options.domain);
294
+ if (!domain) return {
295
+ claim: null,
296
+ error: error != null ? error : 'Invalid domain',
297
+ status: 400
298
+ };
299
+ const snapshot = await claimRef(domain).get();
300
+ if (!snapshot.exists || snapshot.get('orgId') !== orgId) {
301
+ return {
302
+ claim: null,
303
+ error: 'No claim on that domain',
304
+ status: 404
305
+ };
306
+ }
307
+ if (!await orgIsEntitled(orgId)) {
308
+ return {
309
+ claim: null,
310
+ error: 'A custom console domain requires the Agency plan',
311
+ status: 403
312
+ };
313
+ }
314
+ const token = String((_snapshot_get = snapshot.get('txtToken')) != null ? _snapshot_get : '');
315
+ const records = await resolveChallengeTxt(domain);
316
+ const proved = recordsProveOwnership(records, token);
317
+ const status = proved ? snapshot.get('status') === 'active' ? 'active' : 'verified' : 'pending';
318
+ await claimRef(domain).set({
319
+ status,
320
+ lastRecords: records.slice(0, 10),
321
+ verifiedAt: proved ? (_snapshot_get1 = snapshot.get('verifiedAt')) != null ? _snapshot_get1 : firebaseAdmin.firestore.FieldValue.serverTimestamp() : null,
322
+ updatedAt: firebaseAdmin.firestore.FieldValue.serverTimestamp()
323
+ }, {
324
+ merge: true
325
+ });
326
+ const view = claimView(domain, _extends({}, snapshot.data(), {
327
+ status,
328
+ txtToken: token,
329
+ lastRecords: records
330
+ }));
331
+ return proved ? {
332
+ claim: view,
333
+ error: null,
334
+ status: 200
335
+ } : {
336
+ claim: view,
337
+ error: `No matching TXT record at ${view.recordHost}`,
338
+ status: 409
339
+ };
340
+ }
341
+ /**
342
+ * Attach every claimed name to the console Vercel project.
343
+ *
344
+ * ⚠️ Not called from anywhere. It exists so the mechanism is complete and
345
+ * tested; wiring it to a route is 1099c's job and must not happen before 1099d
346
+ * (see the header).
347
+ *
348
+ * Refuses unless the claim is this org's **and** currently verified, and unless
349
+ * the org still passes `whiteLabel`. The names come from the stored claim, not
350
+ * from `consoleDomainNames`, so a change to the twin rule can never attach a
351
+ * name the transaction did not reserve.
352
+ *
353
+ * Twins are registered as per-domain **redirects to the primary**, and the
354
+ * redirect value is a **bare hostname** — `attachProjectDomain` enforces that,
355
+ * because `https://${target}` is rejected by Vercel with a message that blames
356
+ * the target for being absent rather than the format (AGL-1365).
357
+ */ export async function activateConsoleDomain(options) {
358
+ var _ref, _snapshot_get, _allowlist_detail;
359
+ const orgId = String((_ref = options == null ? void 0 : options.orgId) != null ? _ref : '');
360
+ const { domain, error } = validateConsoleDomain(options == null ? void 0 : options.domain);
361
+ if (!domain) {
362
+ return {
363
+ claim: null,
364
+ error: error != null ? error : 'Invalid domain',
365
+ status: 400,
366
+ attachment: null
367
+ };
368
+ }
369
+ const snapshot = await claimRef(domain).get();
370
+ if (!snapshot.exists || snapshot.get('orgId') !== orgId) {
371
+ return {
372
+ claim: null,
373
+ error: 'No claim on that domain',
374
+ status: 404,
375
+ attachment: null
376
+ };
377
+ }
378
+ if (!await orgIsEntitled(orgId)) {
379
+ return {
380
+ claim: null,
381
+ error: 'A custom console domain requires the Agency plan',
382
+ status: 403,
383
+ attachment: null
384
+ };
385
+ }
386
+ const status = snapshot.get('status');
387
+ if (status !== 'verified' && status !== 'active') {
388
+ return {
389
+ claim: null,
390
+ error: 'Prove ownership of the domain before attaching it',
391
+ status: 409,
392
+ attachment: null
393
+ };
394
+ }
395
+ const names = Array.isArray(snapshot.get('names')) ? snapshot.get('names') : [
396
+ domain
397
+ ];
398
+ const primary = names[0];
399
+ const results = [];
400
+ for (const [index, name] of names.entries()){
401
+ results.push(await attachProjectDomain(name, index === 0 ? {} : {
402
+ redirectTo: primary
403
+ }));
404
+ }
405
+ const attached = results.every((result)=>result.outcome === 'attached' || result.outcome === 'already-exists');
406
+ const uploadCorsRemedy = pendingUploadCorsRemedy(results);
407
+ // AGL-1378. Vercel first, deliberately: allowlisting a name we do not
408
+ // actually serve would hand that origin the right to use our App Check site
409
+ // key for nothing in return. Only the primary — a twin is a 308 and never
410
+ // executes the console.
411
+ //
412
+ // If this loses, the claim stays `verified`. A domain reported ready while
413
+ // unlisted is the failure that costs a customer a day of "permission
414
+ // denied" tickets against rules that are perfectly correct.
415
+ const allowlist = attached ? await allowConsoleOrigin(primary) : {
416
+ outcome: 'failed',
417
+ domain: primary,
418
+ detail: 'Vercel did not accept every name'
419
+ };
420
+ const ready = attached && allowlistSatisfied(allowlist.outcome);
421
+ await claimRef(domain).set({
422
+ status: ready ? 'active' : 'verified',
423
+ vercelState: attached ? 'attached' : 'pending',
424
+ appCheckState: allowlist.outcome,
425
+ activatedAt: ready ? (_snapshot_get = snapshot.get('activatedAt')) != null ? _snapshot_get : firebaseAdmin.firestore.FieldValue.serverTimestamp() : null,
426
+ // Bumped on every activation so a session minted against a previous
427
+ // attachment of the same name is not honoured (AGL-1353 D6).
428
+ sessionEpoch: Date.now(),
429
+ updatedAt: firebaseAdmin.firestore.FieldValue.serverTimestamp()
430
+ }, {
431
+ merge: true
432
+ });
433
+ const view = claimView(domain, _extends({}, snapshot.data(), {
434
+ status: ready ? 'active' : 'verified'
435
+ }));
436
+ return {
437
+ claim: view,
438
+ error: ready ? null : attached ? `The domain is attached but cannot pass App Check: ${(_allowlist_detail = allowlist.detail) != null ? _allowlist_detail : allowlist.outcome}` : 'Vercel did not accept every name',
439
+ status: ready ? 200 : 502,
440
+ attachment: {
441
+ domain: primary,
442
+ results,
443
+ attached,
444
+ ready,
445
+ uploadCorsRemedy,
446
+ allowlist
447
+ }
448
+ };
449
+ }
450
+ /**
451
+ * `sessionEpoch = now` across every name a claim covers.
452
+ *
453
+ * The revocation lever of AGL-1353 D6, factored out because D7 pulls it twice:
454
+ * `suspendOnDowngrade` writes it alongside the suspension, and a release writes
455
+ * it alone, before anything else happens. Epoch-ms via `Date.now()`, never
456
+ * `serverTimestamp()` — the value is compared against a cookie's `iat` and an
457
+ * `authorizedAt`, both plain numbers, and a sentinel that resolves later would
458
+ * read as `NaN` at exactly the moment it is supposed to refuse.
459
+ *
460
+ * Never throws, for `suspendOnDowngrade`'s reason: this is defence in depth
461
+ * behind the Vercel domain allowlist, and a caller that cannot persist the bump
462
+ * still has work to do that is more important than the bump.
463
+ */ async function bumpConsoleSessionEpoch(names) {
464
+ if (!Array.isArray(names) || names.length === 0) return;
465
+ try {
466
+ const batch = firestore().batch();
467
+ for (const name of names){
468
+ batch.set(firestore().collection(CONSOLE_DOMAINS_COLLECTION).doc(name), {
469
+ sessionEpoch: Date.now(),
470
+ updatedAt: firebaseAdmin.firestore.FieldValue.serverTimestamp()
471
+ }, {
472
+ merge: true
473
+ });
474
+ }
475
+ await batch.commit();
476
+ } catch (error) {
477
+ console.error('[console-domains] session epoch bump failed', error);
478
+ }
479
+ }
480
+ /**
481
+ * Kill the sessions, detach every name, then drop the claim — in that order.
482
+ *
483
+ * **The epoch bump comes first, and that is D7's own ordering**: *"`sessionEpoch
484
+ * = now` first, then Vercel `DELETE`, then delete the doc. Order matters: kill
485
+ * the sessions while we still control the host."* It was the one step of that
486
+ * sentence this function never performed, while
487
+ * {@link consoleSessionEpochRefuses} documented it as something release
488
+ * already did.
489
+ *
490
+ * Two things went wrong without it, and neither is covered by the Vercel
491
+ * detach:
492
+ *
493
+ * - **The in-flight window.** Vercel's `DELETE` and the allowlist reclaim are
494
+ * network round trips. Across all of them the claim still reads `active`, so
495
+ * `resolveConsoleDomain` says `servable` and `redeemConsoleHandoff` — which
496
+ * sits on `/api/*`, outside the middleware matcher — happily cashes an
497
+ * authorized handoff into a brand-new session on a domain we are in the
498
+ * middle of giving up.
499
+ * - **And that session then outlives the release entirely**, because the
500
+ * document carrying the epoch is deleted a moment later and
501
+ * `readConsoleSessionEpoch` reads a missing document as `0`, which every
502
+ * caller treats as "this control has nothing to say" and FAILS OPEN. The
503
+ * effect was that `suspendOnDowngrade` — the *lesser* action — revoked
504
+ * sessions properly while an explicit release did not revoke them at all.
505
+ *
506
+ * Bumping first inverts that: an authorization that predates the bump is
507
+ * refused `revoked` at redemption, so the window closes rather than merely
508
+ * being short. What survives the release is still only Vercel's allowlist,
509
+ * which is the boundary; this is the defence in depth behind it that D7 asked
510
+ * for and the code had stopped providing.
511
+ *
512
+ * Vercel before the documents, though, for the reason the header gives: a claim
513
+ * released while the name is still on the project is the unindexed-name hole,
514
+ * opened deliberately. If a detach fails the claim is kept and the caller is
515
+ * told, so the name stays reserved to the org that still holds it in Vercel —
516
+ * and the epoch bump is exactly the right residue on that path too, since
517
+ * sessions on a domain we just tried to release should not survive the attempt.
518
+ */ export async function releaseConsoleDomain(options) {
519
+ var _ref;
520
+ const orgId = String((_ref = options == null ? void 0 : options.orgId) != null ? _ref : '');
521
+ const { domain } = validateConsoleDomain(options == null ? void 0 : options.domain);
522
+ if (!domain) return {
523
+ released: false,
524
+ results: [],
525
+ error: 'Invalid domain'
526
+ };
527
+ const snapshot = await claimRef(domain).get();
528
+ if (!snapshot.exists || snapshot.get('orgId') !== orgId) {
529
+ return {
530
+ released: false,
531
+ results: [],
532
+ error: 'No claim on that domain'
533
+ };
534
+ }
535
+ const names = Array.isArray(snapshot.get('names')) ? snapshot.get('names') : [
536
+ domain
537
+ ];
538
+ // D7, step one. Across EVERY name, so a twin cannot outlive its primary —
539
+ // the same reasoning `suspendOnDowngrade` uses, and the same failure posture:
540
+ // never throw. A bump that does not persist still leaves the detach below to
541
+ // run, and refusing to release over a transient Firestore error would leave
542
+ // the domain both attached AND serving, which is the worse of the two.
543
+ await bumpConsoleSessionEpoch(names);
544
+ const results = [];
545
+ for (const name of names)results.push(await detachProjectDomain(name));
546
+ const clear = results.every((result)=>result.outcome === 'detached' || result.outcome === 'not-found' || result.outcome === 'skipped');
547
+ if (!clear) {
548
+ return {
549
+ released: false,
550
+ results,
551
+ error: 'The domain is still attached to the project'
552
+ };
553
+ }
554
+ // AGL-1378, the reclaim half. Same ordering rule as Vercel: give the
555
+ // capability back before dropping the record of it. A claim deleted while
556
+ // the name is still on the reCAPTCHA key leaves an origin we no longer
557
+ // serve holding a permanent right to mint App Check tokens against our site
558
+ // key, with nothing left in Firestore that says why it is there — and it
559
+ // silently spends one of the 250 slots forever.
560
+ const allowlist = await reclaimConsoleOrigin(names[0]);
561
+ if (!allowlistSatisfied(allowlist.outcome)) {
562
+ var _allowlist_detail;
563
+ return {
564
+ released: false,
565
+ results,
566
+ allowlist,
567
+ error: `The domain is off Vercel but still on the App Check allowlist: ${(_allowlist_detail = allowlist.detail) != null ? _allowlist_detail : allowlist.outcome}`
568
+ };
569
+ }
570
+ const batch = firestore().batch();
571
+ for (const name of names){
572
+ batch.delete(firestore().collection(CONSOLE_DOMAINS_COLLECTION).doc(name));
573
+ }
574
+ await batch.commit();
575
+ return {
576
+ released: true,
577
+ results,
578
+ allowlist,
579
+ error: null
580
+ };
581
+ }
582
+ /**
583
+ * Drop a claim that was never attached, without touching Vercel.
584
+ *
585
+ * The counterpart to claiming from the branding form: an org that clears or
586
+ * changes `customConsoleDomain` must not leave its old reservation standing,
587
+ * or it locks another org out of a name nobody is using. Deliberately refuses
588
+ * once the claim has proved ownership or reached Vercel — releasing *that*
589
+ * needs `releaseConsoleDomain`, because a claim dropped while the name is
590
+ * still on the project is the unindexed-name hole in the header.
591
+ */ export async function releasePendingConsoleDomain(options) {
592
+ var _ref;
593
+ const orgId = String((_ref = options == null ? void 0 : options.orgId) != null ? _ref : '');
594
+ const domain = normalizeConsoleDomain(options == null ? void 0 : options.domain);
595
+ if (!orgId || !domain) return false;
596
+ const snapshot = await claimRef(domain).get();
597
+ if (!snapshot.exists || snapshot.get('orgId') !== orgId) return false;
598
+ if (snapshot.get('status') !== 'pending') return false;
599
+ if (snapshot.get('vercelState') === 'attached') return false;
600
+ const names = Array.isArray(snapshot.get('names')) ? snapshot.get('names') : [
601
+ domain
602
+ ];
603
+ const batch = firestore().batch();
604
+ for (const name of names){
605
+ batch.delete(firestore().collection(CONSOLE_DOMAINS_COLLECTION).doc(name));
606
+ }
607
+ await batch.commit();
608
+ return true;
609
+ }
610
+ const UNKNOWN_VERDICT = {
611
+ known: false,
612
+ servable: false,
613
+ orgSlug: null,
614
+ reason: 'unknown',
615
+ degraded: false
616
+ };
617
+ const DEGRADED_VERDICT = {
618
+ known: false,
619
+ servable: false,
620
+ orgSlug: null,
621
+ reason: 'degraded',
622
+ degraded: true
623
+ };
624
+ /**
625
+ * Stop serving a domain whose org no longer holds `whiteLabel`, and make it
626
+ * stick.
627
+ *
628
+ * **This is the fail-closed half of the entitlement, and it is a write on a
629
+ * read path on purpose.** Nothing in the codebase reacts to a plan change —
630
+ * the Stripe webhook writes `plan` and says outright that entitlements resolve
631
+ * at read time, which is sufficient for rendering and insufficient for a
632
+ * hostname (AGL-1353 D7). Rather than add a fan-out to the webhook or a cron
633
+ * that has to be believed, the first request to arrive after a downgrade
634
+ * converts the read-time verdict into stored state: `status: 'suspended'` plus
635
+ * a `sessionEpoch` bump, across **every** name the claim covers, so the twin
636
+ * cannot outlive its primary.
637
+ *
638
+ * The epoch is what makes this more than cosmetic. A refusal that only lives
639
+ * in a verdict leaves already-minted cookies valid; bumping the epoch is the
640
+ * lever that invalidates them at our boundary (AGL-1353 D6).
641
+ *
642
+ * Deliberately NOT the inverse: re-upgrading does not re-activate. Activation
643
+ * attaches names to Vercel and — until AGL-1378 resolves — depends on a manual
644
+ * App Check allowlist entry, so it stays an explicit act through
645
+ * `activateConsoleDomain`, never a side effect of a plan read.
646
+ *
647
+ * Never throws: a suspension that fails to persist still refuses this request,
648
+ * and the next one tries again.
649
+ */ async function suspendOnDowngrade(domain, snapshot) {
650
+ const status = snapshot.get('status');
651
+ if (status === 'suspended') return;
652
+ const names = Array.isArray(snapshot.get('names')) ? snapshot.get('names') : [
653
+ domain
654
+ ];
655
+ try {
656
+ const batch = firestore().batch();
657
+ for (const name of names){
658
+ batch.set(firestore().collection(CONSOLE_DOMAINS_COLLECTION).doc(name), {
659
+ status: 'suspended',
660
+ suspendedReason: 'entitlement',
661
+ sessionEpoch: Date.now(),
662
+ suspendedAt: firebaseAdmin.firestore.FieldValue.serverTimestamp(),
663
+ updatedAt: firebaseAdmin.firestore.FieldValue.serverTimestamp()
664
+ }, {
665
+ merge: true
666
+ });
667
+ }
668
+ await batch.commit();
669
+ } catch (error) {
670
+ console.error('[console-domains] suspend on downgrade failed', error);
671
+ }
672
+ }
673
+ /**
674
+ * Host → org, for the middleware gate and the session-route boundary.
675
+ *
676
+ * **One host pins exactly one org.** That is enforced by the document being
677
+ * keyed on the host and claimed transactionally (see the header), not by
678
+ * anything here — this function only ever reads the pin, and there is no input
679
+ * by which a caller can ask for a different org.
680
+ *
681
+ * The three "serve it anyway" answers, and why each is right:
682
+ *
683
+ * - **`unknown`** — no claim on this host. localhost, preview deployments and
684
+ * self-hosted installs all land here, and they must keep working. It is safe
685
+ * because of the detach ordering `releaseConsoleDomain` enforces: Vercel
686
+ * first, documents second, and the claim is kept if the detach fails. So a
687
+ * name the console project still answers for always has a document, and a
688
+ * name with no document cannot reach this deployment through the console
689
+ * project at all (AGL-1430's correspondence — do not break it).
690
+ * - **`degraded`** — Firestore could not be reached. Fails OPEN, matching the
691
+ * workspace gate and for the reason AGL-1135 recorded: the Vercel domain
692
+ * allowlist is the boundary, and a customer's console going dark because a
693
+ * lookup timed out is worse than the residual exposure. Never cached.
694
+ * - a claim that is `active` and entitled.
695
+ *
696
+ * Everything else refuses. In particular a `verified` claim is **not**
697
+ * servable: until AGL-1378 clears, activation includes a manual App Check
698
+ * allowlist entry, so "ownership proved" must never imply "serve it".
699
+ */ export async function resolveConsoleDomain(host) {
700
+ var _snapshot_get, _ref;
701
+ const domain = normalizeConsoleDomain(host);
702
+ if (!domain) return UNKNOWN_VERDICT;
703
+ let snapshot;
704
+ try {
705
+ snapshot = await claimRef(domain).get();
706
+ } catch (unused) {
707
+ return DEGRADED_VERDICT;
708
+ }
709
+ if (!snapshot.exists) return UNKNOWN_VERDICT;
710
+ const orgId = String((_snapshot_get = snapshot.get('orgId')) != null ? _snapshot_get : '');
711
+ if (!orgId) return _extends({}, UNKNOWN_VERDICT, {
712
+ known: true,
713
+ reason: 'no-org'
714
+ });
715
+ let orgSnapshot;
716
+ try {
717
+ orgSnapshot = await firestore().collection('orgs').doc(orgId).get();
718
+ } catch (unused) {
719
+ return DEGRADED_VERDICT;
720
+ }
721
+ // A claim pointing at an org that no longer exists refuses, but is NOT
722
+ // suspended: `checkEntitlement(undefined)` resolves to the free plan, so
723
+ // writing a suspension off a missing document would be recording an
724
+ // entitlement verdict that was never actually read (the loading-default
725
+ // class this repo keeps hitting). Refuse now, decide when there is data.
726
+ if (!orgSnapshot.exists) {
727
+ return _extends({}, UNKNOWN_VERDICT, {
728
+ known: true,
729
+ reason: 'no-org'
730
+ });
731
+ }
732
+ const org = orgSnapshot.data();
733
+ const orgSlug = String((_ref = org == null ? void 0 : org.slug) != null ? _ref : '') || null;
734
+ if (!checkEntitlement(org, 'whiteLabel')) {
735
+ await suspendOnDowngrade(domain, snapshot);
736
+ return {
737
+ known: true,
738
+ servable: false,
739
+ orgSlug,
740
+ reason: 'not-entitled',
741
+ degraded: false
742
+ };
743
+ }
744
+ if (snapshot.get('status') !== 'active') {
745
+ return {
746
+ known: true,
747
+ servable: false,
748
+ orgSlug,
749
+ reason: 'not-active',
750
+ degraded: false
751
+ };
752
+ }
753
+ return {
754
+ known: true,
755
+ servable: true,
756
+ orgSlug,
757
+ reason: 'active',
758
+ degraded: false
759
+ };
760
+ }
761
+ /**
762
+ * The org a console domain belongs to, or null.
763
+ *
764
+ * Read-only, and used today only to refuse a `customConsoleDomain` that another
765
+ * org already holds. Routing reads `resolveConsoleDomain` instead, which asks
766
+ * the entitlement question this one does not.
767
+ */ export async function getConsoleDomainClaim(domain) {
768
+ var _snapshot_get, _snapshot_get1;
769
+ const normalized = normalizeConsoleDomain(domain);
770
+ if (!normalized) return null;
771
+ const snapshot = await claimRef(normalized).get();
772
+ if (!snapshot.exists) return null;
773
+ return {
774
+ orgId: String((_snapshot_get = snapshot.get('orgId')) != null ? _snapshot_get : ''),
775
+ status: (_snapshot_get1 = snapshot.get('status')) != null ? _snapshot_get1 : 'pending'
776
+ };
777
+ }
778
+
779
+ //# sourceMappingURL=console-domains.js.map