@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,140 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * ID-token revocation on every server door (AGL-1881).
19
+ *
20
+ * ## The gap this closes
21
+ *
22
+ * `verifyIdToken(token)` checks a signature and an expiry. It does not ask
23
+ * whether the account was locked, disabled, deleted, de-staffed or signed out
24
+ * five seconds ago — that costs a round trip, so the SDK makes it opt-in via
25
+ * `checkRevoked`. The pre-launch audit found the flag set on 3 of 175 call
26
+ * sites: the session MINT, the session EXCHANGE and the device-revoke route.
27
+ * Everything else — 117 console API routes, plus the marketplace, commerce and
28
+ * bookings plugin servers — took a bare `verifyIdToken`.
29
+ *
30
+ * So `revokeRefreshTokens(uid)` stopped the browser getting a NEW token and
31
+ * did nothing about the one it already held. After a panic user-lock, a staff
32
+ * password reset, an SSO offboard, an org-wide lockdown or a "sign out
33
+ * everywhere", the revoked token kept opening every one of those doors for the
34
+ * remainder of its hour. `device-revocation.ts` meanwhile promised the effect
35
+ * was "immediate for everything that goes through our server", which was the
36
+ * defect worth fixing: the sentence was shipped and it was not true.
37
+ *
38
+ * ## Why it is a cached epoch and not just `checkRevoked: true`
39
+ *
40
+ * `checkRevoked` costs one `accounts:lookup` per verification. Measured
41
+ * against production `aglyn-main` (n=40): p50 95.5ms, p95 144ms — statistically
42
+ * indistinguishable from the p50 85.8ms single Firestore `doc().get()` that
43
+ * every one of these routes already performs, on the same connection from the
44
+ * same machine. So the round trip is not expensive; it is just paid too often.
45
+ * Turned on unconditionally it would multiply Identity Toolkit calls by the
46
+ * whole console request volume, against a project quota, to re-answer a
47
+ * question whose answer changes maybe twice a year per account.
48
+ *
49
+ * The answer is therefore CACHED, on exactly the terms AGL-1522 already set
50
+ * for the panic path: `PLATFORM_TTL_MS`, 15 seconds, LRU-bounded, with the
51
+ * acting process invalidating the uid after its own write. Steady state costs
52
+ * nothing; a cold entry costs one Firestore-read-equivalent; the honest window
53
+ * goes from ~3600s to ≤15s, and to zero on the process that did the revoking.
54
+ * A revocation control whose staleness bound matches the lockdown control it
55
+ * sits beside is one bound to explain, not two.
56
+ *
57
+ * ## What is checked, and in which pool
58
+ *
59
+ * Three refusals, mirroring firebase-admin's own `checkRevoked` so the error
60
+ * codes route handlers already catch keep working unchanged:
61
+ *
62
+ * - `tokensValidAfterTime` after the token's `auth_time` → `id-token-revoked`
63
+ * - `disabled` → `user-disabled` (a user-scope panic lock sets this, and it
64
+ * is the half that does not depend on clock agreement)
65
+ * - the record is GONE → `id-token-revoked` (a deleted account is not a
66
+ * signed-in one; the SDK's own check treats this as revoked too)
67
+ *
68
+ * The lookup is asked of the auth instance the token was verified with, which
69
+ * is the AGL-2005 rule made structural rather than remembered: a tenant-aware
70
+ * auth asks the tenant pool, and a project-pool lookup for an SSO uid — which
71
+ * would answer "never revoked" about a forged twin — cannot be spelled.
72
+ *
73
+ * ## Failure direction
74
+ *
75
+ * An unreachable Identity Toolkit is an outage, not a revocation, and refusing
76
+ * every request in the console during one would be a self-inflicted lockdown.
77
+ * So a transport failure fails OPEN — and is NOT cached, or a single blip
78
+ * would blind the check for a full TTL. `user-not-found` is not a transport
79
+ * failure and fails closed. This is the opposite direction from
80
+ * `deviceRevocationRefuses`, which refuses an undateable credential, and the
81
+ * split is deliberate: "we could not reach the service" and "the service told
82
+ * us this account is gone" are different answers.
83
+ */
84
+ import type { BaseAuth, DecodedIdToken } from 'firebase-admin/auth';
85
+ /**
86
+ * Matches `PLATFORM_TTL_MS` in `lockdown.ts`, and deliberately so — see the
87
+ * module note. One number for how stale a panic action may read.
88
+ */
89
+ export declare const TOKEN_REVOCATION_TTL_MS = 15000;
90
+ /**
91
+ * Bounded so a scan of uids cannot balloon a warm serverless process. Past
92
+ * this many entries the least-recently-USED is evicted; a cache hit refreshes
93
+ * recency, so an active session is not what a scan pushes out. Same bound and
94
+ * same reasoning as `USER_LOCKDOWN_CACHE_MAX`.
95
+ */
96
+ export declare const TOKEN_REVOCATION_CACHE_MAX = 5000;
97
+ /** Thrown for a token the account has invalidated. Code matches the SDK's. */
98
+ export declare class IdTokenRevokedError extends Error {
99
+ readonly code = "auth/id-token-revoked";
100
+ constructor(message?: string);
101
+ }
102
+ /** Thrown for a token whose account is disabled. Code matches the SDK's. */
103
+ export declare class UserDisabledError extends Error {
104
+ readonly code = "auth/user-disabled";
105
+ constructor(message?: string);
106
+ }
107
+ /**
108
+ * Drop one account's cached verdict — called by every path that revokes, so
109
+ * the process that took the action refuses on its very next request instead
110
+ * of waiting out its own TTL. Called bare, clears everything.
111
+ */
112
+ export declare function invalidateTokenRevocationCache(uid?: string, tenantId?: string | null): void;
113
+ /** Full reset, for specs. Production code wants the per-uid form above. */
114
+ export declare function resetTokenRevocationCache(): void;
115
+ /**
116
+ * Does this account's revocation epoch refuse this token?
117
+ *
118
+ * Both sides are floored to whole seconds before comparing.
119
+ * `tokensValidAfterTime` is an RFC-1123 string with second granularity and
120
+ * `auth_time` is a whole second, so comparing in milliseconds would let a
121
+ * token minted in the same second as the revoke read as refused on one
122
+ * process and admitted on the next.
123
+ *
124
+ * @param decoded - the verified ID token
125
+ * @param validAfterMs - epoch ms from `tokensValidAfterTime`, null when never
126
+ * revoked
127
+ */
128
+ export declare function revocationRefuses(decoded: DecodedIdToken, validAfterMs: number | null | undefined): boolean;
129
+ /**
130
+ * Throw if this verified ID token belongs to an account that has since been
131
+ * revoked, disabled or deleted. Resolves silently otherwise, including when
132
+ * the account could not be reached.
133
+ *
134
+ * @param pool - the auth handle the token was verified against; the lookup
135
+ * MUST go to the same one (AGL-2005), and the parameter is named `pool`
136
+ * rather than `auth` because that is the contract — `no-project-level-auth-
137
+ * lookup.spec.ts` reads the receiver name and is right to
138
+ * @param decoded - the token firebase-admin just verified
139
+ */
140
+ export declare function assertIdTokenNotRevoked(pool: Pick<BaseAuth, 'getUser'>, decoded: DecodedIdToken): Promise<void>;
@@ -0,0 +1,255 @@
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
+ * ID-token revocation on every server door (AGL-1881).
18
+ *
19
+ * ## The gap this closes
20
+ *
21
+ * `verifyIdToken(token)` checks a signature and an expiry. It does not ask
22
+ * whether the account was locked, disabled, deleted, de-staffed or signed out
23
+ * five seconds ago — that costs a round trip, so the SDK makes it opt-in via
24
+ * `checkRevoked`. The pre-launch audit found the flag set on 3 of 175 call
25
+ * sites: the session MINT, the session EXCHANGE and the device-revoke route.
26
+ * Everything else — 117 console API routes, plus the marketplace, commerce and
27
+ * bookings plugin servers — took a bare `verifyIdToken`.
28
+ *
29
+ * So `revokeRefreshTokens(uid)` stopped the browser getting a NEW token and
30
+ * did nothing about the one it already held. After a panic user-lock, a staff
31
+ * password reset, an SSO offboard, an org-wide lockdown or a "sign out
32
+ * everywhere", the revoked token kept opening every one of those doors for the
33
+ * remainder of its hour. `device-revocation.ts` meanwhile promised the effect
34
+ * was "immediate for everything that goes through our server", which was the
35
+ * defect worth fixing: the sentence was shipped and it was not true.
36
+ *
37
+ * ## Why it is a cached epoch and not just `checkRevoked: true`
38
+ *
39
+ * `checkRevoked` costs one `accounts:lookup` per verification. Measured
40
+ * against production `aglyn-main` (n=40): p50 95.5ms, p95 144ms — statistically
41
+ * indistinguishable from the p50 85.8ms single Firestore `doc().get()` that
42
+ * every one of these routes already performs, on the same connection from the
43
+ * same machine. So the round trip is not expensive; it is just paid too often.
44
+ * Turned on unconditionally it would multiply Identity Toolkit calls by the
45
+ * whole console request volume, against a project quota, to re-answer a
46
+ * question whose answer changes maybe twice a year per account.
47
+ *
48
+ * The answer is therefore CACHED, on exactly the terms AGL-1522 already set
49
+ * for the panic path: `PLATFORM_TTL_MS`, 15 seconds, LRU-bounded, with the
50
+ * acting process invalidating the uid after its own write. Steady state costs
51
+ * nothing; a cold entry costs one Firestore-read-equivalent; the honest window
52
+ * goes from ~3600s to ≤15s, and to zero on the process that did the revoking.
53
+ * A revocation control whose staleness bound matches the lockdown control it
54
+ * sits beside is one bound to explain, not two.
55
+ *
56
+ * ## What is checked, and in which pool
57
+ *
58
+ * Three refusals, mirroring firebase-admin's own `checkRevoked` so the error
59
+ * codes route handlers already catch keep working unchanged:
60
+ *
61
+ * - `tokensValidAfterTime` after the token's `auth_time` → `id-token-revoked`
62
+ * - `disabled` → `user-disabled` (a user-scope panic lock sets this, and it
63
+ * is the half that does not depend on clock agreement)
64
+ * - the record is GONE → `id-token-revoked` (a deleted account is not a
65
+ * signed-in one; the SDK's own check treats this as revoked too)
66
+ *
67
+ * The lookup is asked of the auth instance the token was verified with, which
68
+ * is the AGL-2005 rule made structural rather than remembered: a tenant-aware
69
+ * auth asks the tenant pool, and a project-pool lookup for an SSO uid — which
70
+ * would answer "never revoked" about a forged twin — cannot be spelled.
71
+ *
72
+ * ## Failure direction
73
+ *
74
+ * An unreachable Identity Toolkit is an outage, not a revocation, and refusing
75
+ * every request in the console during one would be a self-inflicted lockdown.
76
+ * So a transport failure fails OPEN — and is NOT cached, or a single blip
77
+ * would blind the check for a full TTL. `user-not-found` is not a transport
78
+ * failure and fails closed. This is the opposite direction from
79
+ * `deviceRevocationRefuses`, which refuses an undateable credential, and the
80
+ * split is deliberate: "we could not reach the service" and "the service told
81
+ * us this account is gone" are different answers.
82
+ */ /**
83
+ * Matches `PLATFORM_TTL_MS` in `lockdown.ts`, and deliberately so — see the
84
+ * module note. One number for how stale a panic action may read.
85
+ */ export const TOKEN_REVOCATION_TTL_MS = 15000;
86
+ /**
87
+ * Bounded so a scan of uids cannot balloon a warm serverless process. Past
88
+ * this many entries the least-recently-USED is evicted; a cache hit refreshes
89
+ * recency, so an active session is not what a scan pushes out. Same bound and
90
+ * same reasoning as `USER_LOCKDOWN_CACHE_MAX`.
91
+ */ export const TOKEN_REVOCATION_CACHE_MAX = 5000;
92
+ /** Thrown for a token the account has invalidated. Code matches the SDK's. */ export class IdTokenRevokedError extends Error {
93
+ constructor(message = 'The ID token has been revoked.'){
94
+ super(message), this.code = 'auth/id-token-revoked';
95
+ this.name = 'IdTokenRevokedError';
96
+ }
97
+ }
98
+ /** Thrown for a token whose account is disabled. Code matches the SDK's. */ export class UserDisabledError extends Error {
99
+ constructor(message = 'The user record is disabled.'){
100
+ super(message), this.code = 'auth/user-disabled';
101
+ this.name = 'UserDisabledError';
102
+ }
103
+ }
104
+ const cache = new Map();
105
+ const pending = new Map();
106
+ /**
107
+ * A uid is only unique WITHIN a pool (AGL-2005), so the pool is part of the
108
+ * key. The separator is a NUL because a tenant id cannot contain one.
109
+ */ function cacheKey(uid, tenantId) {
110
+ return `${tenantId != null ? tenantId : ''}\u0000${uid}`;
111
+ }
112
+ /**
113
+ * Drop one account's cached verdict — called by every path that revokes, so
114
+ * the process that took the action refuses on its very next request instead
115
+ * of waiting out its own TTL. Called bare, clears everything.
116
+ */ export function invalidateTokenRevocationCache(uid, tenantId) {
117
+ if (uid === undefined) {
118
+ cache.clear();
119
+ pending.clear();
120
+ return;
121
+ }
122
+ // A revoke reaches the account, not one pool's view of it: clear the
123
+ // project-pool key and the named tenant's, so a caller that does not know
124
+ // which pool answered still gets a fresh read.
125
+ for (const key of [
126
+ cacheKey(uid, null),
127
+ cacheKey(uid, tenantId)
128
+ ]){
129
+ cache.delete(key);
130
+ pending.delete(key);
131
+ }
132
+ }
133
+ /** Full reset, for specs. Production code wants the per-uid form above. */ export function resetTokenRevocationCache() {
134
+ cache.clear();
135
+ pending.clear();
136
+ }
137
+ /**
138
+ * Does this account's revocation epoch refuse this token?
139
+ *
140
+ * Both sides are floored to whole seconds before comparing.
141
+ * `tokensValidAfterTime` is an RFC-1123 string with second granularity and
142
+ * `auth_time` is a whole second, so comparing in milliseconds would let a
143
+ * token minted in the same second as the revoke read as refused on one
144
+ * process and admitted on the next.
145
+ *
146
+ * @param decoded - the verified ID token
147
+ * @param validAfterMs - epoch ms from `tokensValidAfterTime`, null when never
148
+ * revoked
149
+ */ export function revocationRefuses(decoded, validAfterMs) {
150
+ var _ref, _ref1;
151
+ const validAfter = Number(validAfterMs != null ? validAfterMs : 0);
152
+ if (!Number.isFinite(validAfter) || validAfter <= 0) return false;
153
+ // `auth_time` moves only on a real authentication, which is what makes it
154
+ // the right clock: an attacker holding a stolen cookie and refresh token
155
+ // cannot produce a newer one. `iat` is the fallback for the handful of
156
+ // custom-token sign-ins that carry no `auth_time`.
157
+ const issuedSeconds = Number((_ref = (_ref1 = decoded == null ? void 0 : decoded.auth_time) != null ? _ref1 : decoded == null ? void 0 : decoded.iat) != null ? _ref : 0);
158
+ // An undateable token cannot be shown to postdate the revocation, and the
159
+ // safe direction for a security control is to refuse it.
160
+ if (!Number.isFinite(issuedSeconds) || issuedSeconds <= 0) return true;
161
+ return Math.floor(issuedSeconds) < Math.floor(validAfter / 1000);
162
+ }
163
+ /**
164
+ * Read (and cache) the account state behind a uid.
165
+ *
166
+ * `null` means "could not ask" — an outage, distinct from every answer the
167
+ * service can give, and never cached.
168
+ */ async function readAccountState(pool, uid, key) {
169
+ const cached = cache.get(key);
170
+ if (cached && Date.now() - cached.at < TOKEN_REVOCATION_TTL_MS) {
171
+ // Re-insert to refresh recency: eviction is least-recently-used, and an
172
+ // ACTIVE session must not be what a uid scan evicts.
173
+ cache.delete(key);
174
+ cache.set(key, cached);
175
+ return cached.state;
176
+ }
177
+ let inFlight = pending.get(key);
178
+ if (!inFlight) {
179
+ inFlight = (async ()=>{
180
+ // No initializer: every path below either assigns `state` or returns,
181
+ // so seeding it with `null` would be a value nothing ever reads.
182
+ let state;
183
+ try {
184
+ const record = await pool.getUser(uid);
185
+ const validAfterRaw = record == null ? void 0 : record.tokensValidAfterTime;
186
+ const parsed = validAfterRaw ? Date.parse(validAfterRaw) : NaN;
187
+ state = {
188
+ missing: false,
189
+ disabled: (record == null ? void 0 : record.disabled) === true,
190
+ validAfterMs: Number.isFinite(parsed) ? parsed : null
191
+ };
192
+ } catch (error) {
193
+ var _ref;
194
+ const code = (_ref = error == null ? void 0 : error.code) != null ? _ref : '';
195
+ if (code === 'auth/user-not-found') {
196
+ state = {
197
+ missing: true,
198
+ disabled: false,
199
+ validAfterMs: null
200
+ };
201
+ } else {
202
+ // Outage. Fail open, and do NOT cache — one blip must not blind
203
+ // the check for a whole TTL.
204
+ return null;
205
+ }
206
+ }
207
+ cache.delete(key);
208
+ cache.set(key, {
209
+ at: Date.now(),
210
+ state
211
+ });
212
+ while(cache.size > TOKEN_REVOCATION_CACHE_MAX){
213
+ const oldest = cache.keys().next().value;
214
+ if (oldest === undefined) break;
215
+ cache.delete(oldest);
216
+ }
217
+ return state;
218
+ })().finally(()=>{
219
+ pending.delete(key);
220
+ });
221
+ pending.set(key, inFlight);
222
+ }
223
+ return inFlight;
224
+ }
225
+ /**
226
+ * Throw if this verified ID token belongs to an account that has since been
227
+ * revoked, disabled or deleted. Resolves silently otherwise, including when
228
+ * the account could not be reached.
229
+ *
230
+ * @param pool - the auth handle the token was verified against; the lookup
231
+ * MUST go to the same one (AGL-2005), and the parameter is named `pool`
232
+ * rather than `auth` because that is the contract — `no-project-level-auth-
233
+ * lookup.spec.ts` reads the receiver name and is right to
234
+ * @param decoded - the token firebase-admin just verified
235
+ */ export async function assertIdTokenNotRevoked(pool, decoded) {
236
+ var _ref;
237
+ var _decoded_firebase;
238
+ const uid = decoded == null ? void 0 : decoded.uid;
239
+ if (!uid) return;
240
+ const tenantId = (_ref = decoded == null ? void 0 : (_decoded_firebase = decoded.firebase) == null ? void 0 : _decoded_firebase.tenant) != null ? _ref : null;
241
+ const state = await readAccountState(pool, uid, cacheKey(uid, tenantId));
242
+ // `=== null`, not `!state`: strictNullChecks is off repo-wide, so the falsy
243
+ // test would not narrow and would also swallow a real state object one
244
+ // refactor from now.
245
+ if (state === null) return;
246
+ if (state.missing) {
247
+ throw new IdTokenRevokedError('The user record no longer exists.');
248
+ }
249
+ if (state.disabled) throw new UserDisabledError();
250
+ if (revocationRefuses(decoded, state.validAfterMs)) {
251
+ throw new IdTokenRevokedError();
252
+ }
253
+ }
254
+
255
+ //# sourceMappingURL=token-revocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/token-revocation.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 * ID-token revocation on every server door (AGL-1881).\n *\n * ## The gap this closes\n *\n * `verifyIdToken(token)` checks a signature and an expiry. It does not ask\n * whether the account was locked, disabled, deleted, de-staffed or signed out\n * five seconds ago — that costs a round trip, so the SDK makes it opt-in via\n * `checkRevoked`. The pre-launch audit found the flag set on 3 of 175 call\n * sites: the session MINT, the session EXCHANGE and the device-revoke route.\n * Everything else — 117 console API routes, plus the marketplace, commerce and\n * bookings plugin servers — took a bare `verifyIdToken`.\n *\n * So `revokeRefreshTokens(uid)` stopped the browser getting a NEW token and\n * did nothing about the one it already held. After a panic user-lock, a staff\n * password reset, an SSO offboard, an org-wide lockdown or a \"sign out\n * everywhere\", the revoked token kept opening every one of those doors for the\n * remainder of its hour. `device-revocation.ts` meanwhile promised the effect\n * was \"immediate for everything that goes through our server\", which was the\n * defect worth fixing: the sentence was shipped and it was not true.\n *\n * ## Why it is a cached epoch and not just `checkRevoked: true`\n *\n * `checkRevoked` costs one `accounts:lookup` per verification. Measured\n * against production `aglyn-main` (n=40): p50 95.5ms, p95 144ms — statistically\n * indistinguishable from the p50 85.8ms single Firestore `doc().get()` that\n * every one of these routes already performs, on the same connection from the\n * same machine. So the round trip is not expensive; it is just paid too often.\n * Turned on unconditionally it would multiply Identity Toolkit calls by the\n * whole console request volume, against a project quota, to re-answer a\n * question whose answer changes maybe twice a year per account.\n *\n * The answer is therefore CACHED, on exactly the terms AGL-1522 already set\n * for the panic path: `PLATFORM_TTL_MS`, 15 seconds, LRU-bounded, with the\n * acting process invalidating the uid after its own write. Steady state costs\n * nothing; a cold entry costs one Firestore-read-equivalent; the honest window\n * goes from ~3600s to ≤15s, and to zero on the process that did the revoking.\n * A revocation control whose staleness bound matches the lockdown control it\n * sits beside is one bound to explain, not two.\n *\n * ## What is checked, and in which pool\n *\n * Three refusals, mirroring firebase-admin's own `checkRevoked` so the error\n * codes route handlers already catch keep working unchanged:\n *\n * - `tokensValidAfterTime` after the token's `auth_time` → `id-token-revoked`\n * - `disabled` → `user-disabled` (a user-scope panic lock sets this, and it\n * is the half that does not depend on clock agreement)\n * - the record is GONE → `id-token-revoked` (a deleted account is not a\n * signed-in one; the SDK's own check treats this as revoked too)\n *\n * The lookup is asked of the auth instance the token was verified with, which\n * is the AGL-2005 rule made structural rather than remembered: a tenant-aware\n * auth asks the tenant pool, and a project-pool lookup for an SSO uid — which\n * would answer \"never revoked\" about a forged twin — cannot be spelled.\n *\n * ## Failure direction\n *\n * An unreachable Identity Toolkit is an outage, not a revocation, and refusing\n * every request in the console during one would be a self-inflicted lockdown.\n * So a transport failure fails OPEN — and is NOT cached, or a single blip\n * would blind the check for a full TTL. `user-not-found` is not a transport\n * failure and fails closed. This is the opposite direction from\n * `deviceRevocationRefuses`, which refuses an undateable credential, and the\n * split is deliberate: \"we could not reach the service\" and \"the service told\n * us this account is gone\" are different answers.\n */\n\nimport type { BaseAuth, DecodedIdToken } from 'firebase-admin/auth'\n\n/**\n * Matches `PLATFORM_TTL_MS` in `lockdown.ts`, and deliberately so — see the\n * module note. One number for how stale a panic action may read.\n */\nexport const TOKEN_REVOCATION_TTL_MS = 15_000\n\n/**\n * Bounded so a scan of uids cannot balloon a warm serverless process. Past\n * this many entries the least-recently-USED is evicted; a cache hit refreshes\n * recency, so an active session is not what a scan pushes out. Same bound and\n * same reasoning as `USER_LOCKDOWN_CACHE_MAX`.\n */\nexport const TOKEN_REVOCATION_CACHE_MAX = 5000\n\n/** Thrown for a token the account has invalidated. Code matches the SDK's. */\nexport class IdTokenRevokedError extends Error {\n readonly code = 'auth/id-token-revoked'\n constructor(message = 'The ID token has been revoked.') {\n super(message)\n this.name = 'IdTokenRevokedError'\n }\n}\n\n/** Thrown for a token whose account is disabled. Code matches the SDK's. */\nexport class UserDisabledError extends Error {\n readonly code = 'auth/user-disabled'\n constructor(message = 'The user record is disabled.') {\n super(message)\n this.name = 'UserDisabledError'\n }\n}\n\n/** What the check needs off the account record, and nothing else. */\ninterface AccountState {\n /** `true` when the record could not be found at all. */\n missing: boolean\n disabled: boolean\n /** `tokensValidAfterTime` as epoch ms; null when never revoked. */\n validAfterMs: number | null\n}\n\nconst cache = new Map<string, { at: number; state: AccountState }>()\nconst pending = new Map<string, Promise<AccountState | null>>()\n\n/**\n * A uid is only unique WITHIN a pool (AGL-2005), so the pool is part of the\n * key. The separator is a NUL because a tenant id cannot contain one.\n */\nfunction cacheKey(uid: string, tenantId: string | null | undefined): string {\n return `${tenantId ?? ''}\\u0000${uid}`\n}\n\n/**\n * Drop one account's cached verdict — called by every path that revokes, so\n * the process that took the action refuses on its very next request instead\n * of waiting out its own TTL. Called bare, clears everything.\n */\nexport function invalidateTokenRevocationCache(\n uid?: string,\n tenantId?: string | null,\n): void {\n if (uid === undefined) {\n cache.clear()\n pending.clear()\n return\n }\n // A revoke reaches the account, not one pool's view of it: clear the\n // project-pool key and the named tenant's, so a caller that does not know\n // which pool answered still gets a fresh read.\n for (const key of [cacheKey(uid, null), cacheKey(uid, tenantId)]) {\n cache.delete(key)\n pending.delete(key)\n }\n}\n\n/** Full reset, for specs. Production code wants the per-uid form above. */\nexport function resetTokenRevocationCache(): void {\n cache.clear()\n pending.clear()\n}\n\n/**\n * Does this account's revocation epoch refuse this token?\n *\n * Both sides are floored to whole seconds before comparing.\n * `tokensValidAfterTime` is an RFC-1123 string with second granularity and\n * `auth_time` is a whole second, so comparing in milliseconds would let a\n * token minted in the same second as the revoke read as refused on one\n * process and admitted on the next.\n *\n * @param decoded - the verified ID token\n * @param validAfterMs - epoch ms from `tokensValidAfterTime`, null when never\n * revoked\n */\nexport function revocationRefuses(\n decoded: DecodedIdToken,\n validAfterMs: number | null | undefined,\n): boolean {\n const validAfter = Number(validAfterMs ?? 0)\n if (!Number.isFinite(validAfter) || validAfter <= 0) return false\n // `auth_time` moves only on a real authentication, which is what makes it\n // the right clock: an attacker holding a stolen cookie and refresh token\n // cannot produce a newer one. `iat` is the fallback for the handful of\n // custom-token sign-ins that carry no `auth_time`.\n const issuedSeconds = Number(decoded?.auth_time ?? decoded?.iat ?? 0)\n // An undateable token cannot be shown to postdate the revocation, and the\n // safe direction for a security control is to refuse it.\n if (!Number.isFinite(issuedSeconds) || issuedSeconds <= 0) return true\n return Math.floor(issuedSeconds) < Math.floor(validAfter / 1000)\n}\n\n/**\n * Read (and cache) the account state behind a uid.\n *\n * `null` means \"could not ask\" — an outage, distinct from every answer the\n * service can give, and never cached.\n */\nasync function readAccountState(\n pool: Pick<BaseAuth, 'getUser'>,\n uid: string,\n key: string,\n): Promise<AccountState | null> {\n const cached = cache.get(key)\n if (cached && Date.now() - cached.at < TOKEN_REVOCATION_TTL_MS) {\n // Re-insert to refresh recency: eviction is least-recently-used, and an\n // ACTIVE session must not be what a uid scan evicts.\n cache.delete(key)\n cache.set(key, cached)\n return cached.state\n }\n let inFlight = pending.get(key)\n if (!inFlight) {\n inFlight = (async () => {\n // No initializer: every path below either assigns `state` or returns,\n // so seeding it with `null` would be a value nothing ever reads.\n let state: AccountState\n try {\n const record = await pool.getUser(uid)\n const validAfterRaw = record?.tokensValidAfterTime\n const parsed = validAfterRaw ? Date.parse(validAfterRaw) : NaN\n state = {\n missing: false,\n disabled: record?.disabled === true,\n validAfterMs: Number.isFinite(parsed) ? parsed : null,\n }\n } catch (error) {\n const code = (error as { code?: string })?.code ?? ''\n if (code === 'auth/user-not-found') {\n state = { missing: true, disabled: false, validAfterMs: null }\n } else {\n // Outage. Fail open, and do NOT cache — one blip must not blind\n // the check for a whole TTL.\n return null\n }\n }\n cache.delete(key)\n cache.set(key, { at: Date.now(), state })\n while (cache.size > TOKEN_REVOCATION_CACHE_MAX) {\n const oldest = cache.keys().next().value\n if (oldest === undefined) break\n cache.delete(oldest)\n }\n return state\n })().finally(() => {\n pending.delete(key)\n })\n pending.set(key, inFlight)\n }\n return inFlight\n}\n\n/**\n * Throw if this verified ID token belongs to an account that has since been\n * revoked, disabled or deleted. Resolves silently otherwise, including when\n * the account could not be reached.\n *\n * @param pool - the auth handle the token was verified against; the lookup\n * MUST go to the same one (AGL-2005), and the parameter is named `pool`\n * rather than `auth` because that is the contract — `no-project-level-auth-\n * lookup.spec.ts` reads the receiver name and is right to\n * @param decoded - the token firebase-admin just verified\n */\nexport async function assertIdTokenNotRevoked(\n pool: Pick<BaseAuth, 'getUser'>,\n decoded: DecodedIdToken,\n): Promise<void> {\n const uid = decoded?.uid\n if (!uid) return\n const tenantId = decoded?.firebase?.tenant ?? null\n const state = await readAccountState(pool, uid, cacheKey(uid, tenantId))\n // `=== null`, not `!state`: strictNullChecks is off repo-wide, so the falsy\n // test would not narrow and would also swallow a real state object one\n // refactor from now.\n if (state === null) return\n if (state.missing) {\n throw new IdTokenRevokedError('The user record no longer exists.')\n }\n if (state.disabled) throw new UserDisabledError()\n if (revocationRefuses(decoded, state.validAfterMs)) {\n throw new IdTokenRevokedError()\n }\n}\n"],"names":["TOKEN_REVOCATION_TTL_MS","TOKEN_REVOCATION_CACHE_MAX","IdTokenRevokedError","Error","message","code","name","UserDisabledError","cache","Map","pending","cacheKey","uid","tenantId","invalidateTokenRevocationCache","undefined","clear","key","delete","resetTokenRevocationCache","revocationRefuses","decoded","validAfterMs","validAfter","Number","isFinite","issuedSeconds","auth_time","iat","Math","floor","readAccountState","pool","cached","get","Date","now","at","set","state","inFlight","record","getUser","validAfterRaw","tokensValidAfterTime","parsed","parse","NaN","missing","disabled","error","size","oldest","keys","next","value","finally","assertIdTokenNotRevoked","firebase","tenant"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * The slice of `DocumentReference` this needs. Typed structurally so a caller
19
+ * holding a `DocumentReference<T>` from any of the SDK's generic positions
20
+ * passes without a cast, and so specs can drive it with an in-memory double.
21
+ */
22
+ export interface UpdatableDocumentRef {
23
+ update(data: Record<string, unknown>): Promise<unknown>;
24
+ readonly path?: string;
25
+ }
26
+ /**
27
+ * Applies `data` to `ref` only if the document exists.
28
+ *
29
+ * @returns `true` when the update landed, `false` when the document was
30
+ * absent. Any other failure rethrows.
31
+ */
32
+ export declare function updateExisting(ref: UpdatableDocumentRef, data: Record<string, unknown>): Promise<boolean>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * `updateExisting(ref, data)` — write to a document that must ALREADY exist,
18
+ * and say plainly whether it did (AGL-1763).
19
+ *
20
+ * `set(ref, data, { merge: true })` reads as "update if present" and means
21
+ * "create if absent". When the document id came from a caller, a webhook, or a
22
+ * cached list, that gap mints a phantom: a real document holding only the
23
+ * fields of this one patch, which then satisfies every query that filters on
24
+ * them and nothing else. AGL-1755 (a stub booking), AGL-1760 (a stub
25
+ * reservation) and AGL-1763's `orgs/{orgId}` finding are all the same shape.
26
+ *
27
+ * The codebase already carried the correct idiom and even documented the
28
+ * hazard — `apps/tenant/app/api/analytics/collect/route.ts:101`:
29
+ *
30
+ * "update(), not set(): beacons from stale cached pages must not resurrect a
31
+ * deleted overlay as a stats-only stray doc."
32
+ *
33
+ * `update()` IS the existence check: atomic, no second read, and it rejects on
34
+ * a missing document. AGL-1763 measured a lint rule for this and rejected it
35
+ * (14% precision, 57% recall — "this document must already exist" is a
36
+ * property of the caller's intent, not of the syntax). A named call is the
37
+ * remedy instead: it makes the intent explicit and greppable at the site.
38
+ *
39
+ * WHAT THIS ADDS OVER A BARE `.update()`. The tempting shorthand,
40
+ * `ref.update(data).catch(() => false)`, is wrong in a way that is invisible
41
+ * until it matters: it reports "the document was absent" for a permission
42
+ * denial, an App Check rejection, a transport failure, and — the trap that
43
+ * actually bites here — an `INVALID_ARGUMENT` from a delete sentinel below the
44
+ * top level, which `set({ merge: true })` accepts at any depth but `update()`
45
+ * accepts only at the root (`@google-cloud/firestore` serializer,
46
+ * `allowDeletes: 'root'`). Only gRPC `NOT_FOUND` means absent. Everything else
47
+ * rethrows, so a caller that maps `false` onto a 404 cannot turn an outage
48
+ * into a confident lie about the data.
49
+ *
50
+ * WHAT IT DOES NOT DECIDE. Refusing is not automatically right — AGL-1760's
51
+ * lesson is that a refusal must not discard money or work that already
52
+ * happened. The caller still chooses between refusing, refusing-and-recording,
53
+ * and creating deliberately with every required field. This only makes the
54
+ * question unavoidable by handing back the answer.
55
+ */ /** gRPC `Status.NOT_FOUND`. Firestore's "no entity to update" surfaces here. */ const GRPC_NOT_FOUND = 5;
56
+ /**
57
+ * Applies `data` to `ref` only if the document exists.
58
+ *
59
+ * @returns `true` when the update landed, `false` when the document was
60
+ * absent. Any other failure rethrows.
61
+ */ export async function updateExisting(ref, data) {
62
+ try {
63
+ await ref.update(data);
64
+ return true;
65
+ } catch (error) {
66
+ if ((error == null ? void 0 : error.code) === GRPC_NOT_FOUND) {
67
+ return false;
68
+ }
69
+ throw error;
70
+ }
71
+ }
72
+
73
+ //# sourceMappingURL=update-existing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/update-existing.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 * `updateExisting(ref, data)` — write to a document that must ALREADY exist,\n * and say plainly whether it did (AGL-1763).\n *\n * `set(ref, data, { merge: true })` reads as \"update if present\" and means\n * \"create if absent\". When the document id came from a caller, a webhook, or a\n * cached list, that gap mints a phantom: a real document holding only the\n * fields of this one patch, which then satisfies every query that filters on\n * them and nothing else. AGL-1755 (a stub booking), AGL-1760 (a stub\n * reservation) and AGL-1763's `orgs/{orgId}` finding are all the same shape.\n *\n * The codebase already carried the correct idiom and even documented the\n * hazard — `apps/tenant/app/api/analytics/collect/route.ts:101`:\n *\n * \"update(), not set(): beacons from stale cached pages must not resurrect a\n * deleted overlay as a stats-only stray doc.\"\n *\n * `update()` IS the existence check: atomic, no second read, and it rejects on\n * a missing document. AGL-1763 measured a lint rule for this and rejected it\n * (14% precision, 57% recall — \"this document must already exist\" is a\n * property of the caller's intent, not of the syntax). A named call is the\n * remedy instead: it makes the intent explicit and greppable at the site.\n *\n * WHAT THIS ADDS OVER A BARE `.update()`. The tempting shorthand,\n * `ref.update(data).catch(() => false)`, is wrong in a way that is invisible\n * until it matters: it reports \"the document was absent\" for a permission\n * denial, an App Check rejection, a transport failure, and — the trap that\n * actually bites here — an `INVALID_ARGUMENT` from a delete sentinel below the\n * top level, which `set({ merge: true })` accepts at any depth but `update()`\n * accepts only at the root (`@google-cloud/firestore` serializer,\n * `allowDeletes: 'root'`). Only gRPC `NOT_FOUND` means absent. Everything else\n * rethrows, so a caller that maps `false` onto a 404 cannot turn an outage\n * into a confident lie about the data.\n *\n * WHAT IT DOES NOT DECIDE. Refusing is not automatically right — AGL-1760's\n * lesson is that a refusal must not discard money or work that already\n * happened. The caller still chooses between refusing, refusing-and-recording,\n * and creating deliberately with every required field. This only makes the\n * question unavoidable by handing back the answer.\n */\n\n/** gRPC `Status.NOT_FOUND`. Firestore's \"no entity to update\" surfaces here. */\nconst GRPC_NOT_FOUND = 5\n\n/**\n * The slice of `DocumentReference` this needs. Typed structurally so a caller\n * holding a `DocumentReference<T>` from any of the SDK's generic positions\n * passes without a cast, and so specs can drive it with an in-memory double.\n */\nexport interface UpdatableDocumentRef {\n update(data: Record<string, unknown>): Promise<unknown>\n readonly path?: string\n}\n\n/**\n * Applies `data` to `ref` only if the document exists.\n *\n * @returns `true` when the update landed, `false` when the document was\n * absent. Any other failure rethrows.\n */\nexport async function updateExisting(\n ref: UpdatableDocumentRef,\n data: Record<string, unknown>,\n): Promise<boolean> {\n try {\n await ref.update(data)\n return true\n } catch (error) {\n if ((error as { code?: unknown } | null)?.code === GRPC_NOT_FOUND) {\n return false\n }\n throw error\n }\n}\n"],"names":["GRPC_NOT_FOUND","updateExisting","ref","data","update","error","code"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCC,GAED,8EAA8E,GAC9E,MAAMA,iBAAiB;AAYvB;;;;;CAKC,GACD,OAAO,eAAeC,eACpBC,GAAyB,EACzBC,IAA6B;IAE7B,IAAI;QACF,MAAMD,IAAIE,MAAM,CAACD;QACjB,OAAO;IACT,EAAE,OAAOE,OAAO;QACd,IAAI,CAACA,yBAAD,AAACA,MAAqCC,IAAI,MAAKN,gBAAgB;YACjE,OAAO;QACT;QACA,MAAMK;IACR;AACF"}