@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,755 @@
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
+ * Server half of the first-party error beacon (AGL-1538): validates a
19
+ * browser-posted batch of uncaught errors and forwards each to Google Cloud
20
+ * Error Reporting, where grouping, retention, and the alerting policy live.
21
+ *
22
+ * Why Error Reporting rather than a vendor: Google is already a subprocessor
23
+ * and the GCP monitoring/alerting stack already pages (AGL-1502) — a Sentry
24
+ * would add a subprocessor and a DPA line item, which is a deliberately
25
+ * deferred decision. Why a route in between rather than client-direct: the
26
+ * API needs OAuth, and the route is also where the payload is disarmed —
27
+ * clamped, capped, and stripped before anything leaves our origin.
28
+ *
29
+ * The caller is an unauthenticated browser, so every parsed field is clamped
30
+ * here AGAIN regardless of what the client-side scrubber promised — the
31
+ * beacon script is a promise, this is the boundary.
32
+ *
33
+ * Fail-soft throughout: a reporting failure logs one line and returns.
34
+ * The one thing this module must never do is turn an observed error into a
35
+ * served one.
36
+ */ import { getApp } from "firebase-admin/app";
37
+ // Side-effect import: initializes the firebase-admin default app (cert
38
+ // credential) exactly the way firebase-admin.ts does, so `getApp()` below
39
+ // always finds it and its credential can mint the OAuth token.
40
+ import "@aglyn/shared-util-fbserver";
41
+ // The readable half of the server-error signal (AGL-1921) — see the block
42
+ // comment in `reportServerError`.
43
+ import { beaconHealth, deploymentCommitRef, deploymentEnvironmentLabel, isDeployedRuntime, isTransientBeaconCode } from "@aglyn/aglyn/server";
44
+ // The scheme list the beacon and the CSP collector share (AGL-2786). A
45
+ // subpath, not the `/server` barrel: specs that stub that barrel with a
46
+ // hand-built factory would silently turn this into `undefined`.
47
+ import { isForeignScriptStack } from "@aglyn/aglyn/app-utils/foreign-script";
48
+ import { readBeaconHeartbeat, recordBeaconHeartbeat, recordServerError } from "./rate-limit-store.js";
49
+ const MAX_EVENTS_PER_REQUEST = 10;
50
+ const MAX_MESSAGE = 1024;
51
+ const MAX_STACK = 8192;
52
+ const MAX_URL = 512;
53
+ const REPORT_TIMEOUT_MS = 4000;
54
+ function clampString(value, max) {
55
+ return typeof value === 'string' ? value.slice(0, max) : '';
56
+ }
57
+ /** Origin + pathname only, server-enforced — never trust the client scrub. */ function scrubUrl(value) {
58
+ const raw = clampString(value, MAX_URL);
59
+ if (!raw) return '';
60
+ try {
61
+ const url = new URL(raw);
62
+ return `${url.origin}${url.pathname}`;
63
+ } catch (unused) {
64
+ return '';
65
+ }
66
+ }
67
+ /**
68
+ * Was this stack thrown with no code of ours on it, and was all of it seen?
69
+ *
70
+ * Every frame under a scheme no deployment of ours serves (AGL-2786) — an
71
+ * in-app webview's bridge, an extension's content script. The beacon drops
72
+ * these before sending; this is the half that also covers a bundle already
73
+ * cached in a visitor's tab, which keeps posting whatever its copy of the rule
74
+ * missed for as long as the tab lives.
75
+ *
76
+ * Only a stack that arrived WHOLE is judged. One at the clamp may be the head
77
+ * of a longer stack, and the tail it lost could hold the frame that makes the
78
+ * error ours.
79
+ *
80
+ * The beacon's other tell — every frame is the document — is deliberately not
81
+ * applied here. This side has no page to compare against, and our own inline
82
+ * code's handled errors carry the document's URL too.
83
+ */ function isForeignOnlyStack(stack) {
84
+ return stack.length > 0 && stack.length < MAX_STACK && isForeignScriptStack(stack);
85
+ }
86
+ /**
87
+ * The events in a posted payload, clamped and capped. Anything malformed is
88
+ * simply dropped — this endpoint never argues with a browser — and so is an
89
+ * error none of our code threw (see {@link isForeignOnlyStack}).
90
+ */ export function parseClientErrorEvents(payload) {
91
+ const events = payload == null ? void 0 : payload.events;
92
+ if (!Array.isArray(events)) return [];
93
+ const parsed = [];
94
+ for (const entry of events.slice(0, MAX_EVENTS_PER_REQUEST)){
95
+ if (!entry || typeof entry !== 'object') continue;
96
+ const event = entry;
97
+ const message = clampString(event.message, MAX_MESSAGE);
98
+ if (!message) continue;
99
+ const stack = clampString(event.stack, MAX_STACK);
100
+ if (isForeignOnlyStack(stack)) continue;
101
+ parsed.push({
102
+ kind: clampString(event.kind, 32) || 'error',
103
+ message,
104
+ stack: stack || undefined,
105
+ source: scrubUrl(event.source) || undefined,
106
+ line: typeof event.line === 'number' ? Math.trunc(event.line) : undefined,
107
+ url: scrubUrl(event.url) || undefined
108
+ });
109
+ }
110
+ return parsed;
111
+ }
112
+ /**
113
+ * Error Reporting groups by parsing the `message` as a stack trace; an event
114
+ * WITHOUT a stack must carry `context.reportLocation` instead or ingestion
115
+ * drops it. Both shapes are built here so every accepted beacon event is
116
+ * representable.
117
+ */ function toReportedEvent(event, service, version) {
118
+ var _event_line;
119
+ var _event_stack;
120
+ const hasStack = Boolean((_event_stack = event.stack) == null ? void 0 : _event_stack.includes('\n'));
121
+ return {
122
+ '@type': 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent',
123
+ serviceContext: {
124
+ service,
125
+ version
126
+ },
127
+ message: hasStack ? event.stack : `${event.kind}: ${event.message}`,
128
+ // Outside `serviceContext` so Error Reporting ignores it while a log-match
129
+ // policy can still filter on it — the same placement, for the same reason,
130
+ // that the server half gives `route`/`method`/`digest`.
131
+ //
132
+ // Until AGL-2523 the kind reached the payload ONLY on the stackless path,
133
+ // folded into `message` and `reportLocation.functionName`. Every stacked
134
+ // error — which is nearly all of them — arrived unclassifiable, so the
135
+ // `Client error beacon` policy had no way to express anything narrower
136
+ // than "any entry at all". That is why it pages for one visitor whose
137
+ // webview rewrote the DOM.
138
+ kind: clampString(event.kind, 32) || 'error',
139
+ context: _extends({
140
+ httpRequest: event.url ? {
141
+ url: event.url
142
+ } : undefined
143
+ }, hasStack ? {} : {
144
+ reportLocation: {
145
+ filePath: event.source || 'unknown',
146
+ lineNumber: (_event_line = event.line) != null ? _event_line : 0,
147
+ functionName: event.kind
148
+ }
149
+ })
150
+ };
151
+ }
152
+ /** The log the beacon writes; the alert policy and any triage query key on it. */ export const CLIENT_ERROR_LOG_ID = 'client-errors';
153
+ /**
154
+ * The admin credential and project the beacon writes under.
155
+ *
156
+ * Extracted (AGL-1923) so the heartbeat below mints its token through the
157
+ * SAME path the reporter does. That sharing is the whole value of the
158
+ * heartbeat: the failures it exists to catch — an expired service-account
159
+ * key, a revoked `logging.logEntries.create`, an exhausted Logging quota —
160
+ * are environmental, and a heartbeat that acquired its credential some other
161
+ * way could report healthy while every real report was being dropped.
162
+ *
163
+ * Returns null when either half is missing; the caller decides what that
164
+ * means (the reporter drops the batch, the heartbeat reports degraded).
165
+ *
166
+ * EXPORTED for the Vercel log-drain receiver (AGL-1921, third arm), which
167
+ * writes to `vercel-runtime` through this same credential for the same
168
+ * reason the heartbeat does: the failures that would silence the drain are
169
+ * the environmental ones AGL-1923's heartbeat already probes, and it probes
170
+ * them through exactly this path. A receiver that minted its own token would
171
+ * need its own dead-man's switch.
172
+ */ export async function beaconLoggingTarget() {
173
+ return (await resolveBeaconLoggingTarget()).target;
174
+ }
175
+ /**
176
+ * The same resolution, saying which of two very different things went wrong
177
+ * (AGL-2713).
178
+ *
179
+ * `no-credential` used to cover both, and the collapse is what made
180
+ * `/api/health/error-beacon` cry wolf: one of the two is permanent and one is
181
+ * a cold lambda's first outbound connection.
182
+ *
183
+ * - **`no-credential`** — there is no admin app, or it has no project, or it
184
+ * has no credential object. `initializeApp` runs at module load and is
185
+ * guarded on `FIREBASE_PRIVATE_KEY` + `FIREBASE_CLIENT_EMAIL` +
186
+ * `NEXT_PUBLIC_FIREBASE_PROJECT_ID`, so this is a STATIC fact about the
187
+ * deployment's environment: it cannot heal on the next request, and every
188
+ * browser error is being dropped for as long as it holds. Red immediately —
189
+ * which is exactly what the remedy text on the staff board already says
190
+ * (`no-credential` is the deployment `FIREBASE_*` env).
191
+ * - **`credential-unavailable`** — the app and the certificate are both there
192
+ * and `getAccessToken()` did not produce a token on THIS attempt. That is a
193
+ * JWT-bearer exchange against `oauth2.googleapis.com`, and on a cold
194
+ * instance it is the first outbound TLS handshake the sandbox has ever made.
195
+ * Nothing was decided, so nothing about the beacon has been established.
196
+ */ export async function resolveBeaconLoggingTarget() {
197
+ var _app_options_projectId;
198
+ let app;
199
+ try {
200
+ app = getApp();
201
+ } catch (unused) {
202
+ return {
203
+ target: null,
204
+ code: 'no-credential'
205
+ };
206
+ }
207
+ const projectId = (_app_options_projectId = app.options.projectId) != null ? _app_options_projectId : process.env['NEXT_PUBLIC_FIREBASE_PROJECT_ID'];
208
+ const credential = app.options.credential;
209
+ if (!projectId || !credential) return {
210
+ target: null,
211
+ code: 'no-credential'
212
+ };
213
+ let token;
214
+ try {
215
+ var _this;
216
+ token = (_this = await credential.getAccessToken()) == null ? void 0 : _this.access_token;
217
+ } catch (unused) {
218
+ token = undefined;
219
+ }
220
+ if (!token) return {
221
+ target: null,
222
+ code: 'credential-unavailable'
223
+ };
224
+ return {
225
+ target: {
226
+ token,
227
+ projectId
228
+ }
229
+ };
230
+ }
231
+ /**
232
+ * Forwards events under the admin service account — via Cloud LOGGING, not
233
+ * the Error Reporting `events:report` API, and the transport is the point:
234
+ * a log entry whose payload carries the `ReportedErrorEvent` `@type` is
235
+ * ingested by Error Reporting automatically (grouping, the Error Reporting
236
+ * console, version tracking), while ALSO being a real log entry that a
237
+ * Cloud Monitoring log-match alert policy can page on — `events:report`
238
+ * produces no log entry and nothing standard to alert from. One batched
239
+ * write per request instead of one call per event is the bonus.
240
+ *
241
+ * Returns how many events were written; failures are logged (one line) and
242
+ * swallowed.
243
+ */ export async function reportClientErrors(events, options) {
244
+ var _deploymentCommitRef;
245
+ if (!events.length) return 0;
246
+ /*==========================================
247
+ * A LAPTOP IS NOT A DEPLOYMENT (AGL-1925, option 1).
248
+ *
249
+ * Measured 2026-08-18 over a trailing week of Error Reporting `groupStats`:
250
+ * 13 of 17 events — 76% — were not production, and the single highest-ranked
251
+ * "production error group" in the project was a dev-only artifact with
252
+ * `http://localhost:4200` stack frames. Ranking is the whole point of Error
253
+ * Reporting, so a majority-noise stream does not merely cost storage: it
254
+ * aims triage at the wrong thing, and the `Client error beacon` policy
255
+ * notifies its single human recipient for every uncaught error on any
256
+ * developer's machine.
257
+ *
258
+ * The credential is why localhost reaches production at all: `firebase-admin`
259
+ * is configured from the root `.env`, which names the platform project.
260
+ *
261
+ * Unlike the server half this stream carries NO `environment` stamp — the
262
+ * only marker was `serviceContext.version` being absent off Vercel, which
263
+ * did that job by accident and stopped doing it once `deploymentCommitRef`
264
+ * started resolving a commit for self-hosted builds. So refusing the write
265
+ * is not one of two options here; it is the only one that works at all.
266
+ *
267
+ * `isDeployedRuntime`, matching `reportServerError`: an operator's own
268
+ * container reports its visitors' errors, and a developer's console already
269
+ * shows them the error it would have sent.
270
+ *==========================================*/ if (!isDeployedRuntime()) return 0;
271
+ const target = await beaconLoggingTarget();
272
+ const token = target == null ? void 0 : target.token;
273
+ const projectId = target == null ? void 0 : target.projectId;
274
+ if (!token || !projectId) {
275
+ console.warn(JSON.stringify({
276
+ tag: 'AGL-1538:error-beacon',
277
+ drop: events.length,
278
+ reason: 'no-credential'
279
+ }));
280
+ return 0;
281
+ }
282
+ /*
283
+ * Which BUILD this report came from, resolved the way `/api/health` resolves
284
+ * it — `BUILD_ID`, then `COMMIT_REF`, then Vercel's own.
285
+ *
286
+ * This read `VERCEL_GIT_COMMIT_SHA` directly, which is set on Aglyn's cloud
287
+ * and nowhere else, so every error a self-hosted deployment reported arrived
288
+ * with no version at all while the health endpoint beside it named the
289
+ * commit correctly. An error report you cannot tie to a build is an error
290
+ * report you cannot act on.
291
+ *
292
+ * `undefined`, not `null`: this becomes Error Reporting's
293
+ * `serviceContext.version`, where an absent key is omitted and an empty one
294
+ * is a version.
295
+ */ const version = (_deploymentCommitRef = deploymentCommitRef()) != null ? _deploymentCommitRef : undefined;
296
+ try {
297
+ const response = await fetch('https://logging.googleapis.com/v2/entries:write', {
298
+ method: 'POST',
299
+ headers: {
300
+ Authorization: `Bearer ${token}`,
301
+ 'Content-Type': 'application/json'
302
+ },
303
+ body: JSON.stringify({
304
+ logName: `projects/${projectId}/logs/${CLIENT_ERROR_LOG_ID}`,
305
+ resource: {
306
+ type: 'global'
307
+ },
308
+ entries: events.map((event)=>({
309
+ severity: 'ERROR',
310
+ jsonPayload: toReportedEvent(event, options.service, version)
311
+ }))
312
+ }),
313
+ signal: AbortSignal.timeout(REPORT_TIMEOUT_MS)
314
+ });
315
+ if (!response.ok) {
316
+ console.warn(JSON.stringify({
317
+ tag: 'AGL-1538:error-beacon',
318
+ status: response.status,
319
+ drop: events.length
320
+ }));
321
+ return 0;
322
+ }
323
+ return events.length;
324
+ } catch (error) {
325
+ console.warn(JSON.stringify({
326
+ tag: 'AGL-1538:error-beacon',
327
+ transport: String(error).slice(0, 200)
328
+ }));
329
+ return 0;
330
+ }
331
+ }
332
+ /**
333
+ * The log the SERVER half writes to (AGL-1921).
334
+ *
335
+ * DELIBERATELY NOT `client-errors`. The `Client error beacon` policy is a
336
+ * log-match on that log id, and a server 5xx is a different incident with a
337
+ * different first move — a browser error is one visitor's broken render, a
338
+ * 500 rate is every visitor's. Merging the streams would make the existing
339
+ * policy fire for both and force triage to start by asking which it was.
340
+ * A sibling log id also lets the counter this whole issue is about key on
341
+ * server errors ALONE.
342
+ */ export const SERVER_ERROR_LOG_ID = 'server-errors';
343
+ /**
344
+ * Per-instance write budget (AGL-1921).
345
+ *
346
+ * The failure this exists to observe is a SPIKE, and a spike is exactly when
347
+ * an unbounded reporter turns one incident into two — a 500 on a hot path
348
+ * can run at request rate, and every entry is a billable Logging write on an
349
+ * account whose whole monitoring budget is $20/month. The budget is per
350
+ * instance and per minute, so the shape of the spike still reaches Logging
351
+ * (the counter only needs to cross a threshold, not be exact) while the cost
352
+ * stays bounded no matter how hard the incident pushes.
353
+ *
354
+ * Suppression is REPORTED, never silent: when the window rolls over, one
355
+ * summary line records how many were dropped. A monitoring path that hides
356
+ * its own lossiness is the thing this repo keeps finding.
357
+ */ const SERVER_ERROR_BUDGET_PER_WINDOW = 60;
358
+ const SERVER_ERROR_WINDOW_MS = 60000;
359
+ let serverErrorWindowStartedAt = 0;
360
+ let serverErrorsWritten = 0;
361
+ let serverErrorsSuppressed = 0;
362
+ /**
363
+ * Forwards ONE server-side error to Cloud Logging under the admin credential.
364
+ *
365
+ * This is AGL-1921's fallback arm, and it is a fallback on purpose: it cannot
366
+ * see an error that kills the process before the handler runs, nor a
367
+ * platform-level 5xx that never reaches our code at all. A Vercel log drain
368
+ * would see both. What this does buy is the case that matters most and is
369
+ * most likely — our own route handlers and renders throwing — reported
370
+ * through a transport that already works, on an account we already pay for.
371
+ * `docs/UPTIME_AND_SLA.md` carries the blind spots in writing, because
372
+ * partial visibility whose gaps are documented beats none.
373
+ *
374
+ * It shares `beaconLoggingTarget` with the client reporter deliberately: that
375
+ * makes AGL-1923's heartbeat a dead-man's switch for THIS path too. The
376
+ * failures that would silence server-error reporting — expired key, revoked
377
+ * `logging.logEntries.create`, exhausted quota — are the same environmental
378
+ * ones the heartbeat already probes, and it probes them through this exact
379
+ * credential.
380
+ *
381
+ * Fail-soft and never throws: this runs inside `onRequestError`, so throwing
382
+ * here would turn an observed error into a second one during an incident.
383
+ */ export async function reportServerError(event, options) {
384
+ var _deploymentCommitRef;
385
+ const message = clampString(event.message, MAX_MESSAGE);
386
+ if (!message) return 'dropped';
387
+ /*==========================================
388
+ * A LAPTOP IS NOT A DEPLOYMENT, AND THIS IS THE ONLY GATE THAT CAN SAY SO.
389
+ *
390
+ * ABOVE the marker below, unlike every other gate here, and the difference
391
+ * is what is being refused. Those gates are about a write that FAILED and
392
+ * must still be counted. This one is about a process that should not be
393
+ * reporting at all: the count is as wrong as the log entry, because
394
+ * `/api/health/server-errors` reads that marker out of the platform project
395
+ * and would answer for a developer's terminal.
396
+ *
397
+ * The credential makes this reachable. `firebase-admin` is configured from
398
+ * the root `.env`, which names the platform project, so `nx dev` writes to
399
+ * production Cloud Logging under the same key production uses. Measured
400
+ * 2026-08-31: three parse errors from an interrupted rebase — carrying
401
+ * `file:///Users/.../aglyn/apps/...` stacks — landed in `server-errors` and
402
+ * opened the `Server errors: uncaught 5xx` policy, which was still open two
403
+ * days later while all thirteen uptime checks read 100%.
404
+ *
405
+ * `environment` was stamped for this (see the field below), but a stamp only
406
+ * helps a reader who filters on it, and the one reader this log can have is
407
+ * a hand-made alert policy that did not. Refusing the write is the half that
408
+ * does not depend on somebody else's configuration.
409
+ *
410
+ * `isDeployedRuntime` and not `isProductionDeployment`, deliberately: a
411
+ * preview's 5xx is a real 5xx served by a real deployment, and the stamp
412
+ * already separates it for anyone who wants to.
413
+ *==========================================*/ if (!isDeployedRuntime()) return 'dropped';
414
+ /*==========================================
415
+ * COUNT IT WHERE SOMETHING CAN READ THE COUNT (AGL-1921, second pass).
416
+ *
417
+ * FIRST, above every gate below, and that ordering is the whole point.
418
+ *
419
+ * The Logging entry this function goes on to write is the right record for
420
+ * triage and the wrong one for an alarm we can ship: measured 2026-08-24,
421
+ * the production credential is refused `entries:list` on that log with
422
+ * `403 Permission denied for all log views`, so nothing in this repo can
423
+ * read back what it wrote. The only reader the log can ever have is a GCP
424
+ * alert policy somebody creates by hand.
425
+ *
426
+ * The marker is readable today, and putting it above the budget gate, the
427
+ * credential check and the fetch means the count survives every way the
428
+ * Logging arm can fail. That is not tidiness — a beacon whose transport is
429
+ * dead reports ZERO errors, which is the measured-zero shape this repo keeps
430
+ * finding, and it would report it during exactly the incident that killed
431
+ * the transport.
432
+ *
433
+ * Never awaited: this runs inside `onRequestError`, and the marker coalesces
434
+ * in process anyway (at most one write per five seconds per instance).
435
+ *==========================================*/ recordServerError(options.service);
436
+ const now = Date.now();
437
+ if (now - serverErrorWindowStartedAt >= SERVER_ERROR_WINDOW_MS) {
438
+ if (serverErrorsSuppressed > 0) {
439
+ console.warn(JSON.stringify({
440
+ tag: 'AGL-1921:server-error-beacon',
441
+ suppressed: serverErrorsSuppressed,
442
+ written: serverErrorsWritten,
443
+ windowMs: SERVER_ERROR_WINDOW_MS
444
+ }));
445
+ }
446
+ serverErrorWindowStartedAt = now;
447
+ serverErrorsWritten = 0;
448
+ serverErrorsSuppressed = 0;
449
+ }
450
+ if (serverErrorsWritten >= SERVER_ERROR_BUDGET_PER_WINDOW) {
451
+ serverErrorsSuppressed += 1;
452
+ return 'suppressed';
453
+ }
454
+ const target = await beaconLoggingTarget();
455
+ if (!target) {
456
+ console.warn(JSON.stringify({
457
+ tag: 'AGL-1921:server-error-beacon',
458
+ drop: 1,
459
+ reason: 'no-credential'
460
+ }));
461
+ return 'dropped';
462
+ }
463
+ const stack = clampString(event.stack, MAX_STACK);
464
+ const hasStack = stack.includes('\n');
465
+ const version = (_deploymentCommitRef = deploymentCommitRef()) != null ? _deploymentCommitRef : undefined;
466
+ try {
467
+ const response = await fetch('https://logging.googleapis.com/v2/entries:write', {
468
+ method: 'POST',
469
+ headers: {
470
+ Authorization: `Bearer ${target.token}`,
471
+ 'Content-Type': 'application/json'
472
+ },
473
+ body: JSON.stringify({
474
+ logName: `projects/${target.projectId}/logs/${SERVER_ERROR_LOG_ID}`,
475
+ resource: {
476
+ type: 'global'
477
+ },
478
+ entries: [
479
+ {
480
+ severity: 'ERROR',
481
+ jsonPayload: {
482
+ '@type': 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent',
483
+ serviceContext: {
484
+ service: options.service,
485
+ version
486
+ },
487
+ // Same rule as the client half: Error Reporting groups by
488
+ // parsing `message` as a stack trace, and an event without one
489
+ // must carry `context.reportLocation` or ingestion drops it.
490
+ message: hasStack ? stack : message,
491
+ context: _extends({}, hasStack ? {} : {
492
+ reportLocation: {
493
+ filePath: clampString(event.route, MAX_URL) || 'unknown',
494
+ lineNumber: 0,
495
+ functionName: clampString(event.routeType, 32) || 'server'
496
+ }
497
+ }),
498
+ // Outside `serviceContext` so Error Reporting ignores them and
499
+ // a log-match policy can still filter on them.
500
+ route: clampString(event.route, MAX_URL) || undefined,
501
+ method: clampString(event.method, 16) || undefined,
502
+ digest: clampString(event.digest, 64) || undefined,
503
+ // WHICH DEPLOYMENT THREW, and the log-match policy has no other
504
+ // way to ask. The credential comes from the environment, so a
505
+ // developer serving the console locally against the platform
506
+ // project writes here under the same key as production and
507
+ // lands in the same log — a throw on a laptop is then
508
+ // indistinguishable from a 500 served to a customer. The
509
+ // heartbeat beside this already stamps it for that reason.
510
+ environment: deploymentEnvironmentLabel()
511
+ }
512
+ }
513
+ ]
514
+ }),
515
+ signal: AbortSignal.timeout(REPORT_TIMEOUT_MS)
516
+ });
517
+ if (!response.ok) {
518
+ console.warn(JSON.stringify({
519
+ tag: 'AGL-1921:server-error-beacon',
520
+ status: response.status,
521
+ drop: 1
522
+ }));
523
+ return 'dropped';
524
+ }
525
+ serverErrorsWritten += 1;
526
+ return 'written';
527
+ } catch (error) {
528
+ console.warn(JSON.stringify({
529
+ tag: 'AGL-1921:server-error-beacon',
530
+ transport: String(error).slice(0, 200)
531
+ }));
532
+ return 'dropped';
533
+ }
534
+ }
535
+ /**
536
+ * The log the beacon's heartbeat is written to (AGL-1923).
537
+ *
538
+ * DELIBERATELY NOT `client-errors`. The `Client error beacon` alert policy is
539
+ * a log-match on `logName="…/client-errors" AND severity>=ERROR`, so a
540
+ * heartbeat written there would page the on-call owner on every probe — building the
541
+ * alert-fatigue mechanism the heartbeat exists to protect against. A separate
542
+ * log id at INFO keeps the two streams from ever being confused, and keeps
543
+ * the payload out of Error Reporting (which ingests on the
544
+ * `ReportedErrorEvent` `@type`, absent here on purpose).
545
+ */ export const BEACON_HEARTBEAT_LOG_ID = 'client-error-beacon-heartbeat';
546
+ /**
547
+ * Write one heartbeat entry through the beacon's own transport (AGL-1923).
548
+ *
549
+ * ## Why this exists
550
+ *
551
+ * `reportClientErrors` is fail-soft by design and must stay that way — it may
552
+ * never turn an observed error into a served one. But every one of its
553
+ * failure paths ends in a `console.warn` to the Vercel runtime log, which
554
+ * retains about an hour and drains nowhere (AGL-1799). So the beacon can fail
555
+ * completely and the only readings anyone has are "Error Reporting shows zero
556
+ * errors" and "the log-match policy is silent" — which are exactly the
557
+ * readings a clean launch produces. **A dead beacon is indistinguishable from
558
+ * zero errors.**
559
+ *
560
+ * A log-match policy can only ever report presence. The fix is a signal whose
561
+ * ABSENCE is detectable, and the cheapest one that needs no new vendor is a
562
+ * write that a health endpoint can grade synchronously: this returns whether
563
+ * the write landed, `/api/health/error-beacon` turns that into the 200/503
564
+ * contract the sibling health checks already speak, and the existing uptime
565
+ * check + alert + email path becomes the listener. The uptime probe is what
566
+ * winds the dead-man's switch, so there is no cron to forget.
567
+ *
568
+ * ## What it can and cannot prove
569
+ *
570
+ * It proves the credential mints, the IAM grant still covers
571
+ * `logging.logEntries.create`, the Logging API answers, and the quota is not
572
+ * exhausted — the whole environmental failure class AGL-1923 names. It does
573
+ * NOT prove a browser can reach `/api/errors`, and it does not prove the
574
+ * payload shape Error Reporting ingests on, because it deliberately does not
575
+ * write that shape.
576
+ *
577
+ * ## Clearing
578
+ *
579
+ * The condition it raises is an EVENT, not a state: the next successful write
580
+ * clears it, within one probe TTL. Nothing here can latch (AGL-1843).
581
+ *
582
+ * Never throws. A heartbeat that could 500 a health endpoint would be a
583
+ * monitoring probe that causes the outage it reports.
584
+ */ export async function writeBeaconHeartbeat(options) {
585
+ const { target, code } = await resolveBeaconLoggingTarget();
586
+ if (!target) return {
587
+ ok: false,
588
+ code: code != null ? code : 'no-credential'
589
+ };
590
+ try {
591
+ const response = await fetch('https://logging.googleapis.com/v2/entries:write', {
592
+ method: 'POST',
593
+ headers: {
594
+ Authorization: `Bearer ${target.token}`,
595
+ 'Content-Type': 'application/json'
596
+ },
597
+ body: JSON.stringify({
598
+ logName: `projects/${target.projectId}/logs/${BEACON_HEARTBEAT_LOG_ID}`,
599
+ resource: {
600
+ type: 'global'
601
+ },
602
+ entries: [
603
+ {
604
+ severity: 'INFO',
605
+ jsonPayload: {
606
+ // No `@type`: this must not be ingested by Error Reporting.
607
+ tag: 'AGL-1923:beacon-heartbeat',
608
+ service: options.service,
609
+ version: deploymentCommitRef(),
610
+ environment: deploymentEnvironmentLabel()
611
+ }
612
+ }
613
+ ]
614
+ }),
615
+ signal: AbortSignal.timeout(REPORT_TIMEOUT_MS)
616
+ });
617
+ // The STATUS only. The health body is public and a Google error message
618
+ // can carry project ids and resource paths — the same rule the backups
619
+ // probe follows.
620
+ return response.ok ? {
621
+ ok: true
622
+ } : {
623
+ ok: false,
624
+ code: `http-${response.status}`
625
+ };
626
+ } catch (error) {
627
+ var _ref;
628
+ // Codes, not messages: `TimeoutError` for the 4s abort, whatever the
629
+ // fetch layer names for a transport failure.
630
+ return {
631
+ ok: false,
632
+ code: `transport-${String((_ref = error == null ? void 0 : error.name) != null ? _ref : 'unknown')}`
633
+ };
634
+ }
635
+ }
636
+ /**
637
+ * How long the probe waits before its second attempt (AGL-2713).
638
+ *
639
+ * Long enough for a JWT-bearer exchange that lost its first connection to get
640
+ * another one, short enough that it is invisible next to a probe already
641
+ * budgeted at 4s for the Logging write. It is a delay, not a backoff schedule:
642
+ * a second attempt either establishes something or it does not, and a third
643
+ * would only spend a public endpoint's time proving the same point.
644
+ */ export const BEACON_HEARTBEAT_RETRY_DELAY_MS = 250;
645
+ /**
646
+ * How long a missed heartbeat is forgiven after the last one that landed.
647
+ *
648
+ * One monitor interval. The uptime workflow reads this door every 15 minutes,
649
+ * so a grace of exactly that means the FIRST probe after a miss can be
650
+ * forgiven and the second cannot — "tolerate a single miss, red on a
651
+ * sustained one", stated in the only unit the reader actually samples in.
652
+ *
653
+ * The bound this puts on a real outage: the marker is refreshed by every
654
+ * successful probe, and this endpoint is polled every 5 minutes, so the last
655
+ * landing is at most one probe TTL old when a sustained failure starts. Worst
656
+ * case the door reds 20 minutes later than it does today, and the 15-minute
657
+ * workflow sees it on its next run.
658
+ */ export const BEACON_HEARTBEAT_GRACE_MS = 15 * 60000;
659
+ /**
660
+ * One heartbeat probe, graded — the whole of what both doors do (AGL-2713).
661
+ *
662
+ * ## Why this is one function and not two copies
663
+ *
664
+ * `apps/console` and `apps/tenant` each own a copy of
665
+ * `/api/health/error-beacon`, and they must: the deployments carry different
666
+ * admin credentials, so a console heartbeat proves nothing about the tenant
667
+ * one. What they do NOT need is two copies of the policy. Before this the two
668
+ * route files were byte-identical apart from a service name, which is the
669
+ * arrangement that guarantees a fix reaches one door and not the other. The
670
+ * per-deployment part (which credential, which service name, which memo) stays
671
+ * in the routes; the grading lives here, once.
672
+ *
673
+ * ## The sequence, and why each step is where it is
674
+ *
675
+ * 1. Write. If it lands, record the durable marker and report green — the
676
+ * marker being the evidence a LATER miss will be forgiven with.
677
+ * 2. If it did not land and the code is a refusal — `http-401`/`http-403`
678
+ * (a lost `logging.logEntries.create` grant), `http-429` (quota),
679
+ * `no-credential` (the deployment env) — report red NOW, with no retry and
680
+ * no grace. Those are permanent until a person acts, and re-asking a
681
+ * credential that has been revoked only delays the page.
682
+ * 3. Otherwise nothing was decided, so ask once more after a short delay. On a
683
+ * cold lambda this is the step that actually fixes the measured incident:
684
+ * the memo is per-instance, so a fresh instance has no history to be
685
+ * forgiving WITH, and the only tolerance available inside the request is
686
+ * another attempt.
687
+ * 4. If the second attempt misses too, forgive only against the durable
688
+ * marker — see {@link beaconHealth}, which will not forgive without proof.
689
+ *
690
+ * `attempts` and `ms` ride in the body on every outcome, including green ones,
691
+ * because a retry that is quietly always needed is a credential on its way out
692
+ * and a door that hid it would be back where it started.
693
+ */ export async function beaconHeartbeatProbe(options) {
694
+ var _options_now, _options_sleep, _options_graceMs, _options_write, _options_retryDelayMs;
695
+ const clock = (_options_now = options.now) != null ? _options_now : Date.now;
696
+ const pause = (_options_sleep = options.sleep) != null ? _options_sleep : (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
697
+ const graceMs = (_options_graceMs = options.graceMs) != null ? _options_graceMs : BEACON_HEARTBEAT_GRACE_MS;
698
+ const startedAt = clock();
699
+ const service = options.service;
700
+ const write = (_options_write = options.write) != null ? _options_write : writeBeaconHeartbeat;
701
+ const attempt = async ()=>{
702
+ try {
703
+ return await write({
704
+ service
705
+ });
706
+ } catch (unused) {
707
+ // `writeBeaconHeartbeat` is documented never to throw; this is the belt
708
+ // that keeps a monitoring probe from ever being the outage it reports.
709
+ // A null result is degraded by contract — "we could not determine
710
+ // whether the beacon works" IS the condition this endpoint exists to
711
+ // catch, and unlike a missed write it is a broken contract rather than
712
+ // a quiet network, so it is never one of the tolerated shapes.
713
+ return null;
714
+ }
715
+ };
716
+ const graded = (write, attempts, landedAtMs)=>beaconHealth(write, BEACON_HEARTBEAT_LOG_ID, service, clock() - startedAt, {
717
+ attempts,
718
+ landedAtMs,
719
+ graceMs,
720
+ now: clock()
721
+ });
722
+ const first = await attempt();
723
+ if (first == null ? void 0 : first.ok) {
724
+ const landedAtMs = clock();
725
+ recordBeaconHeartbeat(service, {
726
+ now: landedAtMs,
727
+ firestore: options.firestore
728
+ });
729
+ return graded(first, 1, landedAtMs);
730
+ }
731
+ if (first === null || !isTransientBeaconCode(first.code)) {
732
+ return graded(first, 1, null);
733
+ }
734
+ await pause((_options_retryDelayMs = options.retryDelayMs) != null ? _options_retryDelayMs : BEACON_HEARTBEAT_RETRY_DELAY_MS);
735
+ const second = await attempt();
736
+ if (second == null ? void 0 : second.ok) {
737
+ const landedAtMs = clock();
738
+ recordBeaconHeartbeat(service, {
739
+ now: landedAtMs,
740
+ firestore: options.firestore
741
+ });
742
+ return graded(second, 2, landedAtMs);
743
+ }
744
+ if (second === null || !isTransientBeaconCode(second.code)) {
745
+ return graded(second, 2, null);
746
+ }
747
+ // Both attempts came back with nothing decided. The only thing that can
748
+ // forgive that is proof another instance landed a heartbeat recently, and
749
+ // a store that cannot answer is not proof.
750
+ return graded(second, 2, await readBeaconHeartbeat(service, {
751
+ firestore: options.firestore
752
+ }));
753
+ }
754
+
755
+ //# sourceMappingURL=client-error-report.js.map