@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,47 @@
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 RenderedHostEmail } from '@aglyn/shared-util-email';
18
+ /**
19
+ * `renderHostEmail` with `host.*` tokens resolved (AGL-1022).
20
+ *
21
+ * The whole point of host variables is that a publisher's email template can
22
+ * say "write to {{host.supportEmail}}" and have the installing site fill it in.
23
+ * That only happens if something resolves the tokens at SEND time, and the
24
+ * email library cannot: `@aglyn/shared-util-email` is `scope:shared` and may
25
+ * only depend on `scope:shared`, while the token registry is aglyn-scoped.
26
+ *
27
+ * So the join happens here, in the one lib that already sits above both. Three
28
+ * alternatives were weighed and this is the one that cannot fail quietly:
29
+ *
30
+ * * A registration hook inside the email library would mean a provider that is
31
+ * never registered resolves every token to nothing, silently, on a path whose
32
+ * output has already been sent to a customer. This repo has shipped a
33
+ * flawless security control with zero callers before; that is the shape.
34
+ * * Threading the map through all eighteen call sites puts the burden on
35
+ * whoever adds the nineteenth, and they will not know.
36
+ * * Moving the registry into a `scope:shared` lib is the cleanest end state and
37
+ * the right thing if a second shared consumer ever appears — but the only
38
+ * candidate today is `shared/util/tools`, which is generic helpers, and
39
+ * domain logic does not belong there.
40
+ *
41
+ * Costs one host read per send. Emails are not a hot path, and the read buys
42
+ * the guarantee that a template's branding is right by construction.
43
+ */
44
+ export declare function renderHostEmailWithTokens(firestore: FirebaseFirestore.Firestore, hostId: string, templateKey: string, merge?: Record<string, string>, options?: {
45
+ origin?: string;
46
+ }): Promise<RenderedHostEmail | null>;
47
+ export default renderHostEmailWithTokens;
@@ -0,0 +1,72 @@
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 { sanitizeAuthorHtml } from "@aglyn/aglyn/app-utils/author-html";
18
+ import { hostTokenMerge } from "@aglyn/aglyn/app-utils/host-tokens";
19
+ import { renderHostEmail } from "@aglyn/shared-util-email";
20
+ /**
21
+ * `renderHostEmail` with `host.*` tokens resolved (AGL-1022).
22
+ *
23
+ * The whole point of host variables is that a publisher's email template can
24
+ * say "write to {{host.supportEmail}}" and have the installing site fill it in.
25
+ * That only happens if something resolves the tokens at SEND time, and the
26
+ * email library cannot: `@aglyn/shared-util-email` is `scope:shared` and may
27
+ * only depend on `scope:shared`, while the token registry is aglyn-scoped.
28
+ *
29
+ * So the join happens here, in the one lib that already sits above both. Three
30
+ * alternatives were weighed and this is the one that cannot fail quietly:
31
+ *
32
+ * * A registration hook inside the email library would mean a provider that is
33
+ * never registered resolves every token to nothing, silently, on a path whose
34
+ * output has already been sent to a customer. This repo has shipped a
35
+ * flawless security control with zero callers before; that is the shape.
36
+ * * Threading the map through all eighteen call sites puts the burden on
37
+ * whoever adds the nineteenth, and they will not know.
38
+ * * Moving the registry into a `scope:shared` lib is the cleanest end state and
39
+ * the right thing if a second shared consumer ever appears — but the only
40
+ * candidate today is `shared/util/tools`, which is generic helpers, and
41
+ * domain logic does not belong there.
42
+ *
43
+ * Costs one host read per send. Emails are not a hot path, and the read buys
44
+ * the guarantee that a template's branding is right by construction.
45
+ */ export async function renderHostEmailWithTokens(firestore, hostId, templateKey, merge = {}, options = {}) {
46
+ let host;
47
+ try {
48
+ const snapshot = await firestore.collection('hosts').doc(hostId).get();
49
+ host = snapshot.exists ? snapshot.data() : null;
50
+ } catch (unused) {
51
+ // A failed read must not stop the send. Every token then resolves to its
52
+ // empty behaviour, which is the same outcome as a site that set nothing —
53
+ // an email with a gap where the address would be, rather than no email.
54
+ host = null;
55
+ }
56
+ return renderHostEmail(firestore, hostId, templateKey, // Host tokens first: an explicit caller value WINS. A sender passing
57
+ // `host.businessName` is naming a specific thing on purpose (a white-label
58
+ // send, a preview with sample data), and this is not the place to overrule
59
+ // it.
60
+ _extends({}, hostTokenMerge(host), merge), // The HTML policy joins here for the same reason the token map does, and
61
+ // it is the same reason: `renderEmailHtml` requires one, `scope:shared`
62
+ // cannot import one, and this lib already sits above both. Supplying it
63
+ // here rather than at each of the nine senders keeps their signatures
64
+ // untouched and gives the tenth one the right policy without knowing it
65
+ // needed to ask.
66
+ _extends({}, options, {
67
+ sanitize: sanitizeAuthorHtml
68
+ }));
69
+ }
70
+ export default renderHostEmailWithTokens;
71
+
72
+ //# sourceMappingURL=host-email-tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/host-email-tokens.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { sanitizeAuthorHtml } from '@aglyn/aglyn/app-utils/author-html'\nimport {\n hostTokenMerge,\n type HostTokenSource,\n} from '@aglyn/aglyn/app-utils/host-tokens'\nimport {\n renderHostEmail,\n type RenderedHostEmail,\n} from '@aglyn/shared-util-email'\nimport { firebaseAdmin } from './firebase-admin'\n\n/**\n * `renderHostEmail` with `host.*` tokens resolved (AGL-1022).\n *\n * The whole point of host variables is that a publisher's email template can\n * say \"write to {{host.supportEmail}}\" and have the installing site fill it in.\n * That only happens if something resolves the tokens at SEND time, and the\n * email library cannot: `@aglyn/shared-util-email` is `scope:shared` and may\n * only depend on `scope:shared`, while the token registry is aglyn-scoped.\n *\n * So the join happens here, in the one lib that already sits above both. Three\n * alternatives were weighed and this is the one that cannot fail quietly:\n *\n * * A registration hook inside the email library would mean a provider that is\n * never registered resolves every token to nothing, silently, on a path whose\n * output has already been sent to a customer. This repo has shipped a\n * flawless security control with zero callers before; that is the shape.\n * * Threading the map through all eighteen call sites puts the burden on\n * whoever adds the nineteenth, and they will not know.\n * * Moving the registry into a `scope:shared` lib is the cleanest end state and\n * the right thing if a second shared consumer ever appears — but the only\n * candidate today is `shared/util/tools`, which is generic helpers, and\n * domain logic does not belong there.\n *\n * Costs one host read per send. Emails are not a hot path, and the read buys\n * the guarantee that a template's branding is right by construction.\n */\nexport async function renderHostEmailWithTokens(\n firestore: FirebaseFirestore.Firestore,\n hostId: string,\n templateKey: string,\n merge: Record<string, string> = {},\n options: { origin?: string } = {},\n): Promise<RenderedHostEmail | null> {\n let host: HostTokenSource | null\n try {\n const snapshot = await firestore.collection('hosts').doc(hostId).get()\n host = snapshot.exists ? (snapshot.data() as HostTokenSource) : null\n } catch {\n // A failed read must not stop the send. Every token then resolves to its\n // empty behaviour, which is the same outcome as a site that set nothing —\n // an email with a gap where the address would be, rather than no email.\n host = null\n }\n return renderHostEmail(\n firestore as never,\n hostId,\n templateKey,\n // Host tokens first: an explicit caller value WINS. A sender passing\n // `host.businessName` is naming a specific thing on purpose (a white-label\n // send, a preview with sample data), and this is not the place to overrule\n // it.\n { ...hostTokenMerge(host), ...merge },\n // The HTML policy joins here for the same reason the token map does, and\n // it is the same reason: `renderEmailHtml` requires one, `scope:shared`\n // cannot import one, and this lib already sits above both. Supplying it\n // here rather than at each of the nine senders keeps their signatures\n // untouched and gives the tenth one the right policy without knowing it\n // needed to ask.\n { ...options, sanitize: sanitizeAuthorHtml },\n )\n}\n\nexport default renderHostEmailWithTokens\n"],"names":["sanitizeAuthorHtml","hostTokenMerge","renderHostEmail","renderHostEmailWithTokens","firestore","hostId","templateKey","merge","options","host","snapshot","collection","doc","get","exists","data","sanitize"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,kBAAkB,QAAQ,qCAAoC;AACvE,SACEC,cAAc,QAET,qCAAoC;AAC3C,SACEC,eAAe,QAEV,2BAA0B;AAGjC;;;;;;;;;;;;;;;;;;;;;;;;;CAyBC,GACD,OAAO,eAAeC,0BACpBC,SAAsC,EACtCC,MAAc,EACdC,WAAmB,EACnBC,QAAgC,CAAC,CAAC,EAClCC,UAA+B,CAAC,CAAC;IAEjC,IAAIC;IACJ,IAAI;QACF,MAAMC,WAAW,MAAMN,UAAUO,UAAU,CAAC,SAASC,GAAG,CAACP,QAAQQ,GAAG;QACpEJ,OAAOC,SAASI,MAAM,GAAIJ,SAASK,IAAI,KAAyB;IAClE,EAAE,eAAM;QACN,yEAAyE;QACzE,0EAA0E;QAC1E,wEAAwE;QACxEN,OAAO;IACT;IACA,OAAOP,gBACLE,WACAC,QACAC,aACA,qEAAqE;IACrE,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM;IACN,aAAKL,eAAeQ,OAAUF,QAC9B,yEAAyE;IACzE,wEAAwE;IACxE,wEAAwE;IACxE,sEAAsE;IACtE,wEAAwE;IACxE,iBAAiB;IACjB,aAAKC;QAASQ,UAAUhB;;AAE5B;AAEA,eAAeG,0BAAyB"}
@@ -0,0 +1,54 @@
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 { FieldValue } from 'firebase-admin/firestore';
18
+ interface HostMeta {
19
+ displayName?: string;
20
+ subdomain?: string;
21
+ favicon?: string;
22
+ }
23
+ /**
24
+ * Shape one projection row. Exported for tests: the favicon rule below is a
25
+ * delete-vs-omit decision that is invisible in review and silent in
26
+ * production when wrong (AGL-1071).
27
+ */
28
+ export declare const membershipRow: (orgId: string, meta: HostMeta | undefined, role: string) => {
29
+ displayName: string;
30
+ nameLower: string;
31
+ favicon: string | FieldValue;
32
+ role: string;
33
+ updatedAt: FieldValue;
34
+ subdomain?: string;
35
+ orgId: string;
36
+ };
37
+ /**
38
+ * Re-sync ONE member's projection rows across all the org's hosts: set a row
39
+ * for every host the member can reach, delete the row for every host they
40
+ * cannot (a role downgrade silently drops hosts). Call after any change to a
41
+ * single member (add / role change / grant / revoke / ownership step).
42
+ */
43
+ export declare function syncMemberHostProjections(orgId: string, uid: string): Promise<void>;
44
+ /**
45
+ * Re-sync ONE host's projection rows across all org members: set a row for
46
+ * every member who can reach it, delete for those who cannot. Call after a
47
+ * host is created, renamed, or its displayName changes.
48
+ */
49
+ export declare function syncHostProjectionForMembers(orgId: string, hostId: string): Promise<void>;
50
+ /** Delete every projection row for a member across the org's hosts (member removed). */
51
+ export declare function deleteMemberHostProjections(orgId: string, uid: string): Promise<void>;
52
+ /** Delete every member's projection row for one host (host erased). */
53
+ export declare function deleteHostProjectionForAllMembers(orgId: string, hostId: string): Promise<void>;
54
+ export {};
@@ -0,0 +1,194 @@
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
+ * Per-user host membership projection (AGL-844): a reverse index at
19
+ * `users/{uid}/hostMemberships/{hostId}` mirroring `hosts/{hostId}.memberRoles`
20
+ * — the hosts a user can reach in an org, denormalized with the name so the
21
+ * site switcher and subdomain→id routing can query + order + name-prefix-search
22
+ * a user's own sites without loading the whole `hosts` collection (which the
23
+ * rules can only serve unordered under a per-user `memberRoles.{uid}` filter).
24
+ *
25
+ * Because it lives under the user's own doc it is owner-readable and already
26
+ * membership-filtered, so `where(orgId==…) orderBy(nameLower|updatedAt)` is a
27
+ * plain composite index (no dynamic map path). Admin-SDK-written only.
28
+ *
29
+ * It is a best-effort convenience index, NOT an authorization source: real
30
+ * access stays gated by `hosts/{hostId}.memberRoles` in the rules, so a stale
31
+ * row at worst shows a site that then 404s on read — never grants access.
32
+ *
33
+ * Writers are scoped, mirroring the `users/{uid}/orgs` reverse index: a member
34
+ * change re-syncs THAT member across the org's hosts; a host change re-syncs
35
+ * THAT host across members. Kept in sync at the same points as `memberRoles`
36
+ * (see `syncHostMemberRoles` callers) plus the erase/displayName gaps (AGL-855).
37
+ */ import { hostRoleFor, isOrgWideMember, nameSearchKey } from "@aglyn/aglyn/server";
38
+ import { FieldValue } from "firebase-admin/firestore";
39
+ import firebaseAdmin from "./firebase-admin.js";
40
+ const firestore = ()=>firebaseAdmin.app().firestore();
41
+ /** Firestore caps a batch at 500 writes; commit in chunks below that. */ const CHUNK = 400;
42
+ const orgHostIds = async (db, orgId)=>{
43
+ var _ref;
44
+ const org = (await db.collection('orgs').doc(orgId).get()).data();
45
+ return Object.keys((_ref = org == null ? void 0 : org.hosts) != null ? _ref : {});
46
+ };
47
+ const readHostMeta = async (db, hostIds)=>{
48
+ if (hostIds.length === 0) return new Map();
49
+ const snaps = await db.getAll(...hostIds.map((id)=>db.collection('hosts').doc(id)));
50
+ return new Map(snaps.map((snap)=>{
51
+ var _snap_get;
52
+ return [
53
+ snap.id,
54
+ {
55
+ displayName: snap.get('displayName'),
56
+ subdomain: snap.get('subdomain'),
57
+ // The switcher renders from the projection, not the host doc, so the
58
+ // favicon has to travel with it (AGL-1071).
59
+ favicon: (_snap_get = snap.get('seo')) == null ? void 0 : _snap_get.favicon
60
+ }
61
+ ];
62
+ }));
63
+ };
64
+ const listMembers = async (db, orgId)=>{
65
+ const snap = await db.collection('orgs').doc(orgId).collection('members').get();
66
+ return snap.docs.map((d)=>_extends({
67
+ $id: d.id
68
+ }, d.data()));
69
+ };
70
+ const membershipRef = (db, uid, hostId)=>db.collection('users').doc(uid).collection('hostMemberships').doc(hostId);
71
+ /**
72
+ * Shape one projection row. Exported for tests: the favicon rule below is a
73
+ * delete-vs-omit decision that is invisible in review and silent in
74
+ * production when wrong (AGL-1071).
75
+ */ export const membershipRow = (orgId, meta, role)=>{
76
+ var _ref;
77
+ const displayName = (_ref = meta == null ? void 0 : meta.displayName) != null ? _ref : '';
78
+ return _extends({
79
+ orgId
80
+ }, (meta == null ? void 0 : meta.subdomain) ? {
81
+ subdomain: meta.subdomain
82
+ } : {}, {
83
+ displayName,
84
+ nameLower: nameSearchKey(displayName),
85
+ // DELETE rather than omit when the site has no favicon (AGL-1071). These
86
+ // rows are written with `{ merge: true }`, so omitting the key leaves
87
+ // whatever was there — clearing a favicon would keep showing the old one
88
+ // in the switcher until something unrelated rewrote the row. The clear
89
+ // path writes `seo.favicon: ''`, so test truthiness, not nullishness.
90
+ favicon: (meta == null ? void 0 : meta.favicon) ? meta.favicon : FieldValue.delete(),
91
+ role,
92
+ updatedAt: FieldValue.serverTimestamp()
93
+ });
94
+ };
95
+ /** Runs a set of write operations in ≤CHUNK-sized batches. */ const commitChunked = async (db, ops)=>{
96
+ for(let i = 0; i < ops.length; i += CHUNK){
97
+ const batch = db.batch();
98
+ for (const op of ops.slice(i, i + CHUNK))op(batch);
99
+ await batch.commit();
100
+ }
101
+ };
102
+ /**
103
+ * Mirror the member's org-wide reach onto their `users/{uid}/orgs/{orgId}`
104
+ * row (AGL-1032), so the console can tell a site collaborator from an
105
+ * org-wide viewer without a second read on every org route.
106
+ *
107
+ * Rides the projection pass rather than being a call the membership APIs
108
+ * each have to remember: every path that changes a member's reach — add,
109
+ * role change, grant, revoke, ownership transfer — already re-syncs their
110
+ * host rows, so one funnel keeps the mirror true and self-heals a row an
111
+ * earlier failure left stale.
112
+ *
113
+ * Only ever UPDATES an existing row. The reverse index is created with the
114
+ * membership and deleted with it; a `set`/merge here on a removed member
115
+ * would resurrect a half-row with no `orgName` or `slug`, and the console
116
+ * renders that as a nameless workspace nobody can open.
117
+ */ async function syncMemberOrgReach(db, orgId, uid, member) {
118
+ if (!member) return;
119
+ const ref = db.collection('users').doc(uid).collection('orgs').doc(orgId);
120
+ const snap = await ref.get();
121
+ if (!snap.exists) return;
122
+ const orgWide = isOrgWideMember(member);
123
+ if (snap.get('orgWide') === orgWide) return;
124
+ await ref.set({
125
+ orgWide
126
+ }, {
127
+ merge: true
128
+ });
129
+ }
130
+ /**
131
+ * Re-sync ONE member's projection rows across all the org's hosts: set a row
132
+ * for every host the member can reach, delete the row for every host they
133
+ * cannot (a role downgrade silently drops hosts). Call after any change to a
134
+ * single member (add / role change / grant / revoke / ownership step).
135
+ */ export async function syncMemberHostProjections(orgId, uid) {
136
+ const db = firestore();
137
+ const [memberSnap, hostIds] = await Promise.all([
138
+ db.collection('orgs').doc(orgId).collection('members').doc(uid).get(),
139
+ orgHostIds(db, orgId)
140
+ ]);
141
+ const member = memberSnap.exists ? _extends({
142
+ $id: uid
143
+ }, memberSnap.data()) : null;
144
+ await syncMemberOrgReach(db, orgId, uid, member);
145
+ const meta = await readHostMeta(db, hostIds);
146
+ const ops = hostIds.map((hostId)=>(batch)=>{
147
+ const role = hostRoleFor(member, hostId);
148
+ const ref = membershipRef(db, uid, hostId);
149
+ if (role) batch.set(ref, membershipRow(orgId, meta.get(hostId), role), {
150
+ merge: true
151
+ });
152
+ else batch.delete(ref);
153
+ });
154
+ await commitChunked(db, ops);
155
+ }
156
+ /**
157
+ * Re-sync ONE host's projection rows across all org members: set a row for
158
+ * every member who can reach it, delete for those who cannot. Call after a
159
+ * host is created, renamed, or its displayName changes.
160
+ */ export async function syncHostProjectionForMembers(orgId, hostId) {
161
+ const db = firestore();
162
+ const [members, meta] = await Promise.all([
163
+ listMembers(db, orgId),
164
+ readHostMeta(db, [
165
+ hostId
166
+ ])
167
+ ]);
168
+ const hostMeta = meta.get(hostId);
169
+ const ops = members.map((member)=>(batch)=>{
170
+ const role = hostRoleFor(member, hostId);
171
+ const ref = membershipRef(db, member.$id, hostId);
172
+ if (role) batch.set(ref, membershipRow(orgId, hostMeta, role), {
173
+ merge: true
174
+ });
175
+ else batch.delete(ref);
176
+ });
177
+ await commitChunked(db, ops);
178
+ }
179
+ /** Delete every projection row for a member across the org's hosts (member removed). */ export async function deleteMemberHostProjections(orgId, uid) {
180
+ const db = firestore();
181
+ const hostIds = await orgHostIds(db, orgId);
182
+ await commitChunked(db, hostIds.map((hostId)=>(batch)=>{
183
+ batch.delete(membershipRef(db, uid, hostId));
184
+ }));
185
+ }
186
+ /** Delete every member's projection row for one host (host erased). */ export async function deleteHostProjectionForAllMembers(orgId, hostId) {
187
+ const db = firestore();
188
+ const members = await listMembers(db, orgId);
189
+ await commitChunked(db, members.map((member)=>(batch)=>{
190
+ batch.delete(membershipRef(db, member.$id, hostId));
191
+ }));
192
+ }
193
+
194
+ //# sourceMappingURL=host-memberships.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/host-memberships.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Per-user host membership projection (AGL-844): a reverse index at\n * `users/{uid}/hostMemberships/{hostId}` mirroring `hosts/{hostId}.memberRoles`\n * — the hosts a user can reach in an org, denormalized with the name so the\n * site switcher and subdomain→id routing can query + order + name-prefix-search\n * a user's own sites without loading the whole `hosts` collection (which the\n * rules can only serve unordered under a per-user `memberRoles.{uid}` filter).\n *\n * Because it lives under the user's own doc it is owner-readable and already\n * membership-filtered, so `where(orgId==…) orderBy(nameLower|updatedAt)` is a\n * plain composite index (no dynamic map path). Admin-SDK-written only.\n *\n * It is a best-effort convenience index, NOT an authorization source: real\n * access stays gated by `hosts/{hostId}.memberRoles` in the rules, so a stale\n * row at worst shows a site that then 404s on read — never grants access.\n *\n * Writers are scoped, mirroring the `users/{uid}/orgs` reverse index: a member\n * change re-syncs THAT member across the org's hosts; a host change re-syncs\n * THAT host across members. Kept in sync at the same points as `memberRoles`\n * (see `syncHostMemberRoles` callers) plus the erase/displayName gaps (AGL-855).\n */\n\nimport {\n hostRoleFor,\n isOrgWideMember,\n nameSearchKey,\n type AglynOrganization,\n type AglynOrgMember,\n} from '@aglyn/aglyn/server'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport firebaseAdmin from './firebase-admin'\n\nconst firestore = () => firebaseAdmin.app().firestore()\n\n/** Firestore caps a batch at 500 writes; commit in chunks below that. */\nconst CHUNK = 400\n\ninterface HostMeta {\n displayName?: string\n subdomain?: string\n favicon?: string\n}\n\nconst orgHostIds = async (\n db: FirebaseFirestore.Firestore,\n orgId: string,\n): Promise<string[]> => {\n const org = (await db.collection('orgs').doc(orgId).get()).data() as\n | AglynOrganization\n | undefined\n return Object.keys(org?.hosts ?? {})\n}\n\nconst readHostMeta = async (\n db: FirebaseFirestore.Firestore,\n hostIds: string[],\n): Promise<Map<string, HostMeta>> => {\n if (hostIds.length === 0) return new Map()\n const snaps = await db.getAll(\n ...hostIds.map((id) => db.collection('hosts').doc(id)),\n )\n return new Map(\n snaps.map((snap) => [\n snap.id,\n {\n displayName: snap.get('displayName'),\n subdomain: snap.get('subdomain'),\n // The switcher renders from the projection, not the host doc, so the\n // favicon has to travel with it (AGL-1071).\n favicon: snap.get('seo')?.favicon,\n },\n ]),\n )\n}\n\nconst listMembers = async (\n db: FirebaseFirestore.Firestore,\n orgId: string,\n): Promise<AglynOrgMember[]> => {\n const snap = await db\n .collection('orgs')\n .doc(orgId)\n .collection('members')\n .get()\n return snap.docs.map((d) => ({ $id: d.id, ...d.data() }) as AglynOrgMember)\n}\n\nconst membershipRef = (\n db: FirebaseFirestore.Firestore,\n uid: string,\n hostId: string,\n) => db.collection('users').doc(uid).collection('hostMemberships').doc(hostId)\n\n/**\n * Shape one projection row. Exported for tests: the favicon rule below is a\n * delete-vs-omit decision that is invisible in review and silent in\n * production when wrong (AGL-1071).\n */\nexport const membershipRow = (\n orgId: string,\n meta: HostMeta | undefined,\n role: string,\n) => {\n const displayName = meta?.displayName ?? ''\n return {\n orgId,\n ...(meta?.subdomain ? { subdomain: meta.subdomain } : {}),\n displayName,\n nameLower: nameSearchKey(displayName),\n // DELETE rather than omit when the site has no favicon (AGL-1071). These\n // rows are written with `{ merge: true }`, so omitting the key leaves\n // whatever was there — clearing a favicon would keep showing the old one\n // in the switcher until something unrelated rewrote the row. The clear\n // path writes `seo.favicon: ''`, so test truthiness, not nullishness.\n favicon: meta?.favicon ? meta.favicon : FieldValue.delete(),\n role,\n updatedAt: FieldValue.serverTimestamp(),\n }\n}\n\n/** Runs a set of write operations in ≤CHUNK-sized batches. */\nconst commitChunked = async (\n db: FirebaseFirestore.Firestore,\n ops: Array<(batch: FirebaseFirestore.WriteBatch) => void>,\n): Promise<void> => {\n for (let i = 0; i < ops.length; i += CHUNK) {\n const batch = db.batch()\n for (const op of ops.slice(i, i + CHUNK)) op(batch)\n await batch.commit()\n }\n}\n\n/**\n * Mirror the member's org-wide reach onto their `users/{uid}/orgs/{orgId}`\n * row (AGL-1032), so the console can tell a site collaborator from an\n * org-wide viewer without a second read on every org route.\n *\n * Rides the projection pass rather than being a call the membership APIs\n * each have to remember: every path that changes a member's reach — add,\n * role change, grant, revoke, ownership transfer — already re-syncs their\n * host rows, so one funnel keeps the mirror true and self-heals a row an\n * earlier failure left stale.\n *\n * Only ever UPDATES an existing row. The reverse index is created with the\n * membership and deleted with it; a `set`/merge here on a removed member\n * would resurrect a half-row with no `orgName` or `slug`, and the console\n * renders that as a nameless workspace nobody can open.\n */\nasync function syncMemberOrgReach(\n db: FirebaseFirestore.Firestore,\n orgId: string,\n uid: string,\n member: AglynOrgMember | null,\n): Promise<void> {\n if (!member) return\n const ref = db.collection('users').doc(uid).collection('orgs').doc(orgId)\n const snap = await ref.get()\n if (!snap.exists) return\n const orgWide = isOrgWideMember(member)\n if (snap.get('orgWide') === orgWide) return\n await ref.set({ orgWide }, { merge: true })\n}\n\n/**\n * Re-sync ONE member's projection rows across all the org's hosts: set a row\n * for every host the member can reach, delete the row for every host they\n * cannot (a role downgrade silently drops hosts). Call after any change to a\n * single member (add / role change / grant / revoke / ownership step).\n */\nexport async function syncMemberHostProjections(\n orgId: string,\n uid: string,\n): Promise<void> {\n const db = firestore()\n const [memberSnap, hostIds] = await Promise.all([\n db.collection('orgs').doc(orgId).collection('members').doc(uid).get(),\n orgHostIds(db, orgId),\n ])\n const member = memberSnap.exists\n ? ({ $id: uid, ...memberSnap.data() } as AglynOrgMember)\n : null\n await syncMemberOrgReach(db, orgId, uid, member)\n const meta = await readHostMeta(db, hostIds)\n const ops = hostIds.map((hostId) => (batch: FirebaseFirestore.WriteBatch) => {\n const role = hostRoleFor(member, hostId)\n const ref = membershipRef(db, uid, hostId)\n if (role) batch.set(ref, membershipRow(orgId, meta.get(hostId), role), { merge: true })\n else batch.delete(ref)\n })\n await commitChunked(db, ops)\n}\n\n/**\n * Re-sync ONE host's projection rows across all org members: set a row for\n * every member who can reach it, delete for those who cannot. Call after a\n * host is created, renamed, or its displayName changes.\n */\nexport async function syncHostProjectionForMembers(\n orgId: string,\n hostId: string,\n): Promise<void> {\n const db = firestore()\n const [members, meta] = await Promise.all([\n listMembers(db, orgId),\n readHostMeta(db, [hostId]),\n ])\n const hostMeta = meta.get(hostId)\n const ops = members.map((member) => (batch: FirebaseFirestore.WriteBatch) => {\n const role = hostRoleFor(member, hostId)\n const ref = membershipRef(db, member.$id, hostId)\n if (role) batch.set(ref, membershipRow(orgId, hostMeta, role), { merge: true })\n else batch.delete(ref)\n })\n await commitChunked(db, ops)\n}\n\n/** Delete every projection row for a member across the org's hosts (member removed). */\nexport async function deleteMemberHostProjections(\n orgId: string,\n uid: string,\n): Promise<void> {\n const db = firestore()\n const hostIds = await orgHostIds(db, orgId)\n await commitChunked(\n db,\n hostIds.map((hostId) => (batch: FirebaseFirestore.WriteBatch) => {\n batch.delete(membershipRef(db, uid, hostId))\n }),\n )\n}\n\n/** Delete every member's projection row for one host (host erased). */\nexport async function deleteHostProjectionForAllMembers(\n orgId: string,\n hostId: string,\n): Promise<void> {\n const db = firestore()\n const members = await listMembers(db, orgId)\n await commitChunked(\n db,\n members.map((member) => (batch: FirebaseFirestore.WriteBatch) => {\n batch.delete(membershipRef(db, member.$id, hostId))\n }),\n )\n}\n"],"names":["hostRoleFor","isOrgWideMember","nameSearchKey","FieldValue","firebaseAdmin","firestore","app","CHUNK","orgHostIds","db","orgId","org","collection","doc","get","data","Object","keys","hosts","readHostMeta","hostIds","length","Map","snaps","getAll","map","id","snap","displayName","subdomain","favicon","listMembers","docs","d","$id","membershipRef","uid","hostId","membershipRow","meta","role","nameLower","delete","updatedAt","serverTimestamp","commitChunked","ops","i","batch","op","slice","commit","syncMemberOrgReach","member","ref","exists","orgWide","set","merge","syncMemberHostProjections","memberSnap","Promise","all","syncHostProjectionForMembers","members","hostMeta","deleteMemberHostProjections","deleteHostProjectionForAllMembers"],"mappings":"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"}