@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,180 @@
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
+ import { type MemberEmailAlias, type MemberEmailAliasAddRefusal } from '@aglyn/aglyn/app-utils/member-email-aliases';
18
+ /** Confirmation emails one member may cause per hour, across addresses. */
19
+ export declare const MEMBER_EMAIL_ALIAS_SENDS_PER_MEMBER_PER_HOUR = 10;
20
+ /**
21
+ * Confirmation emails one address may receive per hour, from anybody.
22
+ * Per member alone would let many members aim at one mailbox; per address
23
+ * alone would let one member walk a list — the two limits the account
24
+ * address route applies, for the same mailbomb shape.
25
+ */
26
+ export declare const MEMBER_EMAIL_ALIAS_SENDS_PER_ADDRESS_PER_HOUR = 3;
27
+ /** Every refusal this module answers with, as a stable code. */
28
+ export type MemberEmailAliasRefusal = MemberEmailAliasAddRefusal | 'not-a-member' | 'unknown-address' | 'token-invalid' | 'token-expired' | 'wrong-member' | 'link-retired';
29
+ export interface MemberEmailAliasRefused {
30
+ ok: false;
31
+ refusal: MemberEmailAliasRefusal;
32
+ message: string;
33
+ }
34
+ /** The addresses one member has added in one workspace, confirmed or not. */
35
+ export declare function listMemberEmailAliases(firestore: FirebaseFirestore.Firestore, orgId: string, uid: string): Promise<MemberEmailAlias[]>;
36
+ export type MemberEmailAliasAdded = {
37
+ ok: true;
38
+ alias: MemberEmailAlias;
39
+ /** False when the address was already waiting for confirmation. */
40
+ created: boolean;
41
+ } | MemberEmailAliasRefused;
42
+ /**
43
+ * Adds an address, unconfirmed, for a member of the workspace — or answers
44
+ * the entry already waiting for it, so the caller sends another link. One
45
+ * transaction over the roster row and the member's document, so the
46
+ * ceiling is counted from what is stored at the moment of the write and an
47
+ * address is never added for somebody who is no longer a member.
48
+ */
49
+ export declare function addMemberEmailAlias(firestore: FirebaseFirestore.Firestore, input: {
50
+ orgId: string;
51
+ uid: string;
52
+ address: unknown;
53
+ /** The address the member signs in with, which is theirs already. */
54
+ signInEmail?: string | null;
55
+ /** Domains the deployment receives mail at — the capture domain. */
56
+ reservedDomains?: readonly string[];
57
+ nowMs?: number;
58
+ }): Promise<MemberEmailAliasAdded>;
59
+ /**
60
+ * Removes one address, confirmed or not; the document goes with its last
61
+ * address. Removal retires every link sent for the address, because a link
62
+ * names the entry's `addedAtMs` and there is no longer an entry to match.
63
+ */
64
+ export type MemberEmailAliasRemoved = {
65
+ ok: true;
66
+ removed: MemberEmailAlias;
67
+ } | MemberEmailAliasRefused;
68
+ export declare function removeMemberEmailAlias(firestore: FirebaseFirestore.Firestore, input: {
69
+ orgId: string;
70
+ uid: string;
71
+ address: unknown;
72
+ nowMs?: number;
73
+ }): Promise<MemberEmailAliasRemoved>;
74
+ /** What a confirmation link asserts. */
75
+ export interface MemberEmailAliasClaims {
76
+ orgId: string;
77
+ uid: string;
78
+ address: string;
79
+ /** The `addedAtMs` of the entry the link was sent for. */
80
+ addedAtMs: number;
81
+ /** Expiry, epoch ms. */
82
+ exp: number;
83
+ }
84
+ /**
85
+ * The token a confirmation link carries for one entry. Throws when
86
+ * `TOKEN_SIGNING_SECRET` is not configured, like every token that secret
87
+ * signs; the route answers that as a failure to send.
88
+ */
89
+ export declare function mintMemberEmailAliasToken(claims: Omit<MemberEmailAliasClaims, 'exp'>, nowMs?: number): string;
90
+ /**
91
+ * The claims a token carries, or why it carries none: `token-expired` for a
92
+ * genuine link past its day, `token-invalid` for anything else — tampered,
93
+ * truncated, another version, or a deployment with no signing secret, whose
94
+ * only safe reading of a link is that it proves nothing.
95
+ */
96
+ export declare function readMemberEmailAliasToken(token: unknown, nowMs?: number): {
97
+ ok: true;
98
+ claims: MemberEmailAliasClaims;
99
+ } | MemberEmailAliasRefused;
100
+ export type MemberEmailAliasConfirmed = {
101
+ ok: true;
102
+ orgId: string;
103
+ address: string;
104
+ /** True when the address had been confirmed before this call. */
105
+ alreadyConfirmed: boolean;
106
+ } | MemberEmailAliasRefused;
107
+ /**
108
+ * Confirms the address a link was sent for, for the member signed in — who
109
+ * must be the member who added it (see the module comment). A link for an
110
+ * entry that has since been removed or added again is refused as retired,
111
+ * and opening a link twice is not an error.
112
+ */
113
+ export declare function confirmMemberEmailAlias(firestore: FirebaseFirestore.Firestore, input: {
114
+ token: unknown;
115
+ callerUid: string;
116
+ nowMs?: number;
117
+ }): Promise<MemberEmailAliasConfirmed>;
118
+ export type MemberEmailAliasesConfirmedByProvider = {
119
+ ok: true;
120
+ /** The addresses this call confirmed, normalized, in list order. */
121
+ confirmed: string[];
122
+ } | MemberEmailAliasRefused;
123
+ /**
124
+ * Confirms a member's PENDING addresses that their connected mail account
125
+ * already proves are theirs (AGL-2978).
126
+ *
127
+ * The confirmation link proves an address receives mail and asks the member
128
+ * to open it. A provider's verified send-as list proves more: the provider
129
+ * confirmed the account may send as the address, and the member proved they
130
+ * hold that account by connecting it under their own session. So an address
131
+ * the member added and has not yet confirmed counts as confirmed once it
132
+ * appears in that list.
133
+ *
134
+ * Only addresses the member ALREADY added are touched — this never adds one,
135
+ * because an address the member did not ask for is not a claim they made —
136
+ * and an address already confirmed keeps its original `verifiedAtMs`. The
137
+ * caller passes only the provider's VERIFIED addresses; which of a
138
+ * provider's listings count as verified is the caller's rule, not this
139
+ * store's. Nothing is written when nothing changes.
140
+ */
141
+ export declare function confirmMemberEmailAliasesByProvider(firestore: FirebaseFirestore.Firestore, input: {
142
+ orgId: string;
143
+ uid: string;
144
+ addresses: readonly unknown[];
145
+ nowMs?: number;
146
+ }): Promise<MemberEmailAliasesConfirmedByProvider>;
147
+ /**
148
+ * The link a confirmation email carries: the console origin the caller
149
+ * resolved from server configuration — never from a request header — the
150
+ * page the member added the address on, and the token.
151
+ */
152
+ export declare function memberEmailAliasConfirmUrl(input: {
153
+ origin: string;
154
+ returnPath?: unknown;
155
+ token: string;
156
+ }): string;
157
+ export type MemberEmailAliasConfirmationSent = {
158
+ sent: true;
159
+ } | {
160
+ sent: false;
161
+ status: 429 | 501 | 502;
162
+ error: string;
163
+ };
164
+ /**
165
+ * Emails the confirmation link to the address, in the workspace's brand.
166
+ *
167
+ * Rate-limited per member and per destination address before anything is
168
+ * sent (see the two ceilings above), and metered to the workspace, whose
169
+ * member asked for it.
170
+ */
171
+ export declare function sendMemberEmailAliasConfirmation(input: {
172
+ orgId: string;
173
+ /** The org document, for its name and its brand. */
174
+ org: Record<string, unknown> | null;
175
+ uid: string;
176
+ /** The member's display name, when the roster row carries one. */
177
+ memberName?: string | null;
178
+ address: string;
179
+ confirmUrl: string;
180
+ }): Promise<MemberEmailAliasConfirmationSent>;
@@ -0,0 +1,420 @@
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
+ */ import { evaluateMemberEmailAliasAdd, isVerifiedMemberEmailAlias, MEMBER_EMAIL_ALIAS_CONFIRM_PARAM, MEMBER_EMAIL_ALIAS_CONFIRM_TTL_MS, MEMBER_EMAIL_ALIASES_COLLECTION, normalizeMemberEmailAlias, readMemberEmailAliases } from "@aglyn/aglyn/app-utils/member-email-aliases";
18
+ import { resolveBrandingProfile } from "@aglyn/aglyn/server";
19
+ import { isEmailConfigured, sendEmail } from "@aglyn/shared-util-email";
20
+ import { createHmac } from "crypto";
21
+ import { tokenSigningSecret } from "./media-signing.js";
22
+ import { safeEqual } from "./safe-equal.js";
23
+ /**
24
+ * A MEMBER'S OWN ADDRESSES (AGL-2975): the half with Firestore.
25
+ *
26
+ * The pure rules — what an address is, which are confirmed, whose an
27
+ * address is — are `@aglyn/aglyn`'s `member-email-aliases.ts`. This module
28
+ * is the store under `orgs/{orgId}/memberEmailAliases/{uid}`, the signed
29
+ * confirmation link, and the email that carries it.
30
+ *
31
+ * ## Every write is here
32
+ *
33
+ * The rules deny the collection to every client, reads included, so
34
+ * `verifiedAtMs` is only ever set here, by one of two writers, each running
35
+ * behind the member's own verified session:
36
+ * {@link confirmMemberEmailAlias}, for a member who opened the link mailed
37
+ * to the address, and {@link confirmMemberEmailAliasesByProvider}, for a
38
+ * member whose connected mail account lists the address as one its provider
39
+ * has already verified they may send as. A client that could write the
40
+ * document could mark any address it liked as its own, and the capture
41
+ * webhook would then file a stranger's mail as the member's.
42
+ *
43
+ * ## The confirmation link
44
+ *
45
+ * `{ org, uid, address, addedAtMs, exp }`, HMAC-signed with the shared,
46
+ * fail-closed `TOKEN_SIGNING_SECRET` under its own `member-email-alias:`
47
+ * prefix, so it can never be replayed as a media, edit-bar or form token.
48
+ * Nothing is stored for it: the entry's `addedAtMs` is what binds a link to
49
+ * one request, so removing an address and adding it again retires every
50
+ * link sent before, and a link for an address already confirmed confirms
51
+ * nothing new.
52
+ *
53
+ * Confirming takes the session of the member who added the address — not
54
+ * merely the link. Opening the mailbox proves the address receives mail;
55
+ * it does not prove the mailbox is the member's. A prospect whose address
56
+ * was pasted into the wrong field receives the link too, and a link that
57
+ * worked for whoever opened it would let that prospect turn their own
58
+ * address into the member's, after which every reply they sent would be
59
+ * filed as the member writing to nobody.
60
+ *
61
+ * Firestore is a parameter, as in `crm-inbound-email.ts`, so a spec needs a
62
+ * store and nothing else; the rate limiter and the email meter are loaded
63
+ * only when a message is actually sent.
64
+ */ /** The version tag at the head of a token, so a later claim change is refused by shape. */ const TOKEN_VERSION = 'mea1';
65
+ /** Domain separation from every other token the shared secret signs. */ const TOKEN_CONTEXT = 'member-email-alias:v1';
66
+ /** Far above any real token, so an oversized value is refused unparsed. */ const TOKEN_MAX_CHARS = 2048;
67
+ /** Confirmation emails one member may cause per hour, across addresses. */ export const MEMBER_EMAIL_ALIAS_SENDS_PER_MEMBER_PER_HOUR = 10;
68
+ /**
69
+ * Confirmation emails one address may receive per hour, from anybody.
70
+ * Per member alone would let many members aim at one mailbox; per address
71
+ * alone would let one member walk a list — the two limits the account
72
+ * address route applies, for the same mailbomb shape.
73
+ */ export const MEMBER_EMAIL_ALIAS_SENDS_PER_ADDRESS_PER_HOUR = 3;
74
+ const refused = (refusal, message)=>({
75
+ ok: false,
76
+ refusal,
77
+ message
78
+ });
79
+ const NOT_A_MEMBER = 'Only a member of this workspace can add addresses to it.';
80
+ function aliasesRef(firestore, orgId, uid) {
81
+ return firestore.collection('orgs').doc(orgId).collection(MEMBER_EMAIL_ALIASES_COLLECTION).doc(uid);
82
+ }
83
+ function memberRef(firestore, orgId, uid) {
84
+ return firestore.collection('orgs').doc(orgId).collection('members').doc(uid);
85
+ }
86
+ /** The addresses one member has added in one workspace, confirmed or not. */ export async function listMemberEmailAliases(firestore, orgId, uid) {
87
+ const snapshot = await aliasesRef(firestore, orgId, uid).get();
88
+ return readMemberEmailAliases(snapshot.exists ? snapshot.data() : null);
89
+ }
90
+ /**
91
+ * Adds an address, unconfirmed, for a member of the workspace — or answers
92
+ * the entry already waiting for it, so the caller sends another link. One
93
+ * transaction over the roster row and the member's document, so the
94
+ * ceiling is counted from what is stored at the moment of the write and an
95
+ * address is never added for somebody who is no longer a member.
96
+ */ export async function addMemberEmailAlias(firestore, input) {
97
+ var _input_nowMs;
98
+ const nowMs = (_input_nowMs = input.nowMs) != null ? _input_nowMs : Date.now();
99
+ const ref = aliasesRef(firestore, input.orgId, input.uid);
100
+ const roster = memberRef(firestore, input.orgId, input.uid);
101
+ return firestore.runTransaction(async (tx)=>{
102
+ var _input_signInEmail, _input_reservedDomains;
103
+ const member = await tx.get(roster);
104
+ if (!member.exists) return refused('not-a-member', NOT_A_MEMBER);
105
+ const stored = await tx.get(ref);
106
+ const aliases = readMemberEmailAliases(stored.exists ? stored.data() : null);
107
+ const verdict = evaluateMemberEmailAliasAdd({
108
+ address: input.address,
109
+ signInEmail: (_input_signInEmail = input.signInEmail) != null ? _input_signInEmail : null,
110
+ aliases,
111
+ reservedDomains: (_input_reservedDomains = input.reservedDomains) != null ? _input_reservedDomains : []
112
+ });
113
+ if (verdict.ok === false) return refused(verdict.refusal, verdict.message);
114
+ if (verdict.existing) return {
115
+ ok: true,
116
+ alias: verdict.existing,
117
+ created: false
118
+ };
119
+ const alias = {
120
+ address: verdict.address,
121
+ addedAtMs: nowMs
122
+ };
123
+ tx.set(ref, {
124
+ uid: input.uid,
125
+ aliases: [
126
+ ...aliases,
127
+ alias
128
+ ],
129
+ updatedAtMs: nowMs
130
+ });
131
+ return {
132
+ ok: true,
133
+ alias,
134
+ created: true
135
+ };
136
+ });
137
+ }
138
+ export async function removeMemberEmailAlias(firestore, input) {
139
+ var _input_nowMs;
140
+ const address = normalizeMemberEmailAlias(input.address);
141
+ if (!address) return refused('invalid-address', 'Enter a valid email address.');
142
+ const nowMs = (_input_nowMs = input.nowMs) != null ? _input_nowMs : Date.now();
143
+ const ref = aliasesRef(firestore, input.orgId, input.uid);
144
+ return firestore.runTransaction(async (tx)=>{
145
+ const stored = await tx.get(ref);
146
+ const aliases = readMemberEmailAliases(stored.exists ? stored.data() : null);
147
+ const removed = aliases.find((alias)=>alias.address === address);
148
+ if (!removed) return refused('unknown-address', 'That address is not on your list.');
149
+ const remaining = aliases.filter((alias)=>alias.address !== address);
150
+ if (remaining.length) {
151
+ tx.set(ref, {
152
+ uid: input.uid,
153
+ aliases: remaining,
154
+ updatedAtMs: nowMs
155
+ });
156
+ } else {
157
+ tx.delete(ref);
158
+ }
159
+ return {
160
+ ok: true,
161
+ removed
162
+ };
163
+ });
164
+ }
165
+ const signature = (payload)=>createHmac('sha256', tokenSigningSecret()).update(`${TOKEN_CONTEXT}:${payload}`).digest('base64url');
166
+ /**
167
+ * The token a confirmation link carries for one entry. Throws when
168
+ * `TOKEN_SIGNING_SECRET` is not configured, like every token that secret
169
+ * signs; the route answers that as a failure to send.
170
+ */ export function mintMemberEmailAliasToken(claims, nowMs = Date.now()) {
171
+ const wire = {
172
+ o: claims.orgId,
173
+ u: claims.uid,
174
+ a: claims.address,
175
+ t: claims.addedAtMs,
176
+ e: nowMs + MEMBER_EMAIL_ALIAS_CONFIRM_TTL_MS
177
+ };
178
+ const payload = Buffer.from(JSON.stringify(wire), 'utf8').toString('base64url');
179
+ return `${TOKEN_VERSION}.${payload}.${signature(payload)}`;
180
+ }
181
+ /**
182
+ * The claims a token carries, or why it carries none: `token-expired` for a
183
+ * genuine link past its day, `token-invalid` for anything else — tampered,
184
+ * truncated, another version, or a deployment with no signing secret, whose
185
+ * only safe reading of a link is that it proves nothing.
186
+ */ export function readMemberEmailAliasToken(token, nowMs = Date.now()) {
187
+ const invalid = refused('token-invalid', 'That confirmation link is not valid.');
188
+ if (typeof token !== 'string' || !token || token.length > TOKEN_MAX_CHARS) return invalid;
189
+ const parts = token.split('.');
190
+ if (parts.length !== 3 || parts[0] !== TOKEN_VERSION) return invalid;
191
+ const [, payload, signed] = parts;
192
+ let expected;
193
+ try {
194
+ expected = signature(payload);
195
+ } catch (unused) {
196
+ return invalid;
197
+ }
198
+ if (!safeEqual(signed, expected)) return invalid;
199
+ let wire;
200
+ try {
201
+ wire = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
202
+ } catch (unused) {
203
+ return invalid;
204
+ }
205
+ const address = normalizeMemberEmailAlias(wire == null ? void 0 : wire.a);
206
+ const orgId = typeof (wire == null ? void 0 : wire.o) === 'string' ? wire.o : '';
207
+ const uid = typeof (wire == null ? void 0 : wire.u) === 'string' ? wire.u : '';
208
+ const addedAtMs = Number(wire == null ? void 0 : wire.t);
209
+ const exp = Number(wire == null ? void 0 : wire.e);
210
+ if (!orgId || !uid || !address || !Number.isFinite(addedAtMs) || !Number.isFinite(exp)) {
211
+ return invalid;
212
+ }
213
+ if (exp <= nowMs) {
214
+ return refused('token-expired', 'That confirmation link has expired. Send a new one from the list.');
215
+ }
216
+ return {
217
+ ok: true,
218
+ claims: {
219
+ orgId,
220
+ uid,
221
+ address,
222
+ addedAtMs,
223
+ exp
224
+ }
225
+ };
226
+ }
227
+ /**
228
+ * Confirms the address a link was sent for, for the member signed in — who
229
+ * must be the member who added it (see the module comment). A link for an
230
+ * entry that has since been removed or added again is refused as retired,
231
+ * and opening a link twice is not an error.
232
+ */ export async function confirmMemberEmailAlias(firestore, input) {
233
+ var _input_nowMs;
234
+ const nowMs = (_input_nowMs = input.nowMs) != null ? _input_nowMs : Date.now();
235
+ const read = readMemberEmailAliasToken(input.token, nowMs);
236
+ if (read.ok === false) return read;
237
+ const { claims } = read;
238
+ if (!input.callerUid || claims.uid !== input.callerUid) {
239
+ return refused('wrong-member', 'This link confirms an address for another member. Sign in as the member who added it, then open the link again.');
240
+ }
241
+ const ref = aliasesRef(firestore, claims.orgId, claims.uid);
242
+ const roster = memberRef(firestore, claims.orgId, claims.uid);
243
+ return firestore.runTransaction(async (tx)=>{
244
+ const member = await tx.get(roster);
245
+ if (!member.exists) return refused('not-a-member', 'You are no longer a member of that workspace.');
246
+ const stored = await tx.get(ref);
247
+ const aliases = readMemberEmailAliases(stored.exists ? stored.data() : null);
248
+ const entry = aliases.find((alias)=>alias.address === claims.address);
249
+ if (!entry || entry.addedAtMs !== claims.addedAtMs) {
250
+ return refused('link-retired', 'This link is for an address that has since been removed or added again. Send a new link from the list.');
251
+ }
252
+ if (isVerifiedMemberEmailAlias(entry)) {
253
+ return {
254
+ ok: true,
255
+ orgId: claims.orgId,
256
+ address: entry.address,
257
+ alreadyConfirmed: true
258
+ };
259
+ }
260
+ const next = aliases.map((alias)=>alias.address === entry.address ? _extends({}, alias, {
261
+ verifiedAtMs: nowMs
262
+ }) : alias);
263
+ tx.set(ref, {
264
+ uid: claims.uid,
265
+ aliases: next,
266
+ updatedAtMs: nowMs
267
+ });
268
+ return {
269
+ ok: true,
270
+ orgId: claims.orgId,
271
+ address: entry.address,
272
+ alreadyConfirmed: false
273
+ };
274
+ });
275
+ }
276
+ /**
277
+ * Confirms a member's PENDING addresses that their connected mail account
278
+ * already proves are theirs (AGL-2978).
279
+ *
280
+ * The confirmation link proves an address receives mail and asks the member
281
+ * to open it. A provider's verified send-as list proves more: the provider
282
+ * confirmed the account may send as the address, and the member proved they
283
+ * hold that account by connecting it under their own session. So an address
284
+ * the member added and has not yet confirmed counts as confirmed once it
285
+ * appears in that list.
286
+ *
287
+ * Only addresses the member ALREADY added are touched — this never adds one,
288
+ * because an address the member did not ask for is not a claim they made —
289
+ * and an address already confirmed keeps its original `verifiedAtMs`. The
290
+ * caller passes only the provider's VERIFIED addresses; which of a
291
+ * provider's listings count as verified is the caller's rule, not this
292
+ * store's. Nothing is written when nothing changes.
293
+ */ export async function confirmMemberEmailAliasesByProvider(firestore, input) {
294
+ var _input_nowMs;
295
+ const nowMs = (_input_nowMs = input.nowMs) != null ? _input_nowMs : Date.now();
296
+ const proven = new Set(input.addresses.map((address)=>normalizeMemberEmailAlias(address)).filter((address)=>Boolean(address)));
297
+ if (!input.orgId || !input.uid) return refused('not-a-member', NOT_A_MEMBER);
298
+ if (!proven.size) return {
299
+ ok: true,
300
+ confirmed: []
301
+ };
302
+ const ref = aliasesRef(firestore, input.orgId, input.uid);
303
+ const roster = memberRef(firestore, input.orgId, input.uid);
304
+ return firestore.runTransaction(async (tx)=>{
305
+ const member = await tx.get(roster);
306
+ if (!member.exists) return refused('not-a-member', NOT_A_MEMBER);
307
+ const stored = await tx.get(ref);
308
+ const aliases = readMemberEmailAliases(stored.exists ? stored.data() : null);
309
+ const confirmed = [];
310
+ const next = aliases.map((alias)=>{
311
+ if (isVerifiedMemberEmailAlias(alias) || !proven.has(alias.address)) return alias;
312
+ confirmed.push(alias.address);
313
+ return _extends({}, alias, {
314
+ verifiedAtMs: nowMs
315
+ });
316
+ });
317
+ if (confirmed.length) {
318
+ tx.set(ref, {
319
+ uid: input.uid,
320
+ aliases: next,
321
+ updatedAtMs: nowMs
322
+ });
323
+ }
324
+ return {
325
+ ok: true,
326
+ confirmed
327
+ };
328
+ });
329
+ }
330
+ /**
331
+ * A console path a link may land on: absolute, on the console's own
332
+ * origin, with no query or fragment of its own. Anything else lands on
333
+ * `/`, which still signs the member in.
334
+ */ const RETURN_PATH = /^\/(?![/\\])[^\s?#\\]{0,511}$/;
335
+ /**
336
+ * The link a confirmation email carries: the console origin the caller
337
+ * resolved from server configuration — never from a request header — the
338
+ * page the member added the address on, and the token.
339
+ */ export function memberEmailAliasConfirmUrl(input) {
340
+ var _input_origin;
341
+ const origin = String((_input_origin = input.origin) != null ? _input_origin : '').replace(/\/+$/, '');
342
+ const raw = typeof input.returnPath === 'string' ? input.returnPath : '';
343
+ const path = RETURN_PATH.test(raw) ? raw : '/';
344
+ return `${origin}${path}?${MEMBER_EMAIL_ALIAS_CONFIRM_PARAM}=${encodeURIComponent(input.token)}`;
345
+ }
346
+ /**
347
+ * Emails the confirmation link to the address, in the workspace's brand.
348
+ *
349
+ * Rate-limited per member and per destination address before anything is
350
+ * sent (see the two ceilings above), and metered to the workspace, whose
351
+ * member asked for it.
352
+ */ export async function sendMemberEmailAliasConfirmation(input) {
353
+ var _input_org, _ref, _input_memberName;
354
+ var _input_org1;
355
+ if (!isEmailConfigured()) {
356
+ return {
357
+ sent: false,
358
+ status: 501,
359
+ error: 'Email is not configured on this deployment.'
360
+ };
361
+ }
362
+ const { consumeRateLimit } = await import("./rate-limit-store.js");
363
+ const perMember = await consumeRateLimit(`member-email-alias-confirm:${input.uid}`, {
364
+ limit: MEMBER_EMAIL_ALIAS_SENDS_PER_MEMBER_PER_HOUR,
365
+ windowMs: 60 * 60 * 1000
366
+ });
367
+ if (!perMember.allowed) {
368
+ return {
369
+ sent: false,
370
+ status: 429,
371
+ error: 'Too many confirmation emails. Try again in an hour.'
372
+ };
373
+ }
374
+ const perAddress = await consumeRateLimit(`member-email-alias-target:${input.address}`, {
375
+ limit: MEMBER_EMAIL_ALIAS_SENDS_PER_ADDRESS_PER_HOUR,
376
+ windowMs: 60 * 60 * 1000
377
+ });
378
+ if (!perAddress.allowed) {
379
+ return {
380
+ sent: false,
381
+ status: 429,
382
+ error: 'Too many confirmation emails for that address. Try again later.'
383
+ };
384
+ }
385
+ const branding = resolveBrandingProfile((_input_org = input.org) != null ? _input_org : null);
386
+ const workspace = String((_ref = (_input_org1 = input.org) == null ? void 0 : _input_org1['name']) != null ? _ref : '').trim() || 'your workspace';
387
+ const member = String((_input_memberName = input.memberName) != null ? _input_memberName : '').trim();
388
+ const text = [
389
+ `${member || 'A member'} asked to add ${input.address} as one of their own email ` + `addresses in the ${workspace} workspace on ${branding.productName}.`,
390
+ '',
391
+ `To confirm it, open this link while signed in as ${member || 'that member'}:`,
392
+ '',
393
+ input.confirmUrl,
394
+ '',
395
+ `Once it is confirmed, email sent from ${input.address} with the workspace's capture ` + 'address in BCC is filed under the person it was written to. The link works for 24 hours.',
396
+ '',
397
+ 'If you did not expect this email, ignore it. Nothing changes unless the member who asked opens the link.'
398
+ ].join('\n');
399
+ const result = await sendEmail({
400
+ to: input.address,
401
+ subject: `Confirm your address for ${workspace}`,
402
+ text,
403
+ fromName: branding.fromName,
404
+ context: 'member-email-alias-confirmation'
405
+ });
406
+ if (!result.sent) {
407
+ return {
408
+ sent: false,
409
+ status: 502,
410
+ error: 'The confirmation email could not be sent.'
411
+ };
412
+ }
413
+ const { meterOrgEmail } = await import("./email-metering.js");
414
+ await meterOrgEmail(input.orgId).catch(()=>undefined);
415
+ return {
416
+ sent: true
417
+ };
418
+ }
419
+
420
+ //# sourceMappingURL=member-email-aliases.js.map