@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,421 @@
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
+ * A dataset record write refreshes the live pages that show it (AGL-3113).
19
+ *
20
+ * ## What was missing
21
+ *
22
+ * Every other change a visitor can see announces itself the moment it lands: a
23
+ * publish, a layout, a component, a content entry, a redirect rule, the
24
+ * maintenance toggle. A dataset RECORD did not. It was written — by a form, by
25
+ * an automation, over `/v1`, from the console — and the pages repeating over
26
+ * it went on serving the rows they were built from until the hour-long window
27
+ * lapsed. `PUBLISHED_SITE_DATA_TTL_SECONDS` was the propagation mechanism,
28
+ * which is the same mistake AGL-1302 and AGL-1150 each had to undo elsewhere:
29
+ * a backstop doing a job nothing else was doing.
30
+ *
31
+ * ## Scope, not the site
32
+ *
33
+ * A record changed one dataset, so this drops the pages that repeat over that
34
+ * dataset and nothing else. The set is derived from the SAME binding the
35
+ * published page expands — `repeatDatasetKeys`, the generic
36
+ * `props.repeatDataset` any node can now carry (AGL-3111) — rather than from a
37
+ * list of components that may repeat. A component allowlist was already wrong
38
+ * the day repeat stopped being the Stack's: a heading bound to a dataset
39
+ * renders rows exactly as a Stack does, and would have been missed.
40
+ *
41
+ * Three corpora, because the composer reads repeat keys after the layout chain
42
+ * and the reusable-component graft: a repeat in a layout's chrome or inside a
43
+ * component reaches every screen that renders it.
44
+ *
45
+ * ## Which sites
46
+ *
47
+ * Datasets are ORG-scoped and shared with sites through `visibleTo` — every
48
+ * site in the org, or up to `MAX_SCOPE_HOSTS` named ones. Each site caches its
49
+ * own copy of the rows under its own `tenant-data:{hostId}` tag and its own
50
+ * page keys, so one record write is a fan-out. A missing `visibleTo` is
51
+ * visible to NOBODY and announces nothing; defaulting it to org-wide here
52
+ * would re-make AGL-1466 in the cache layer.
53
+ *
54
+ * ## Best effort, always
55
+ *
56
+ * The record is already written by the time any of this runs. A refused or
57
+ * failed drop is reported to the caller and logged, never thrown: a record
58
+ * must not be lost because a cache refused, and the TTL is still underneath as
59
+ * the backstop it was always meant to be.
60
+ */ import { ORG_SCOPE_TOKEN, hostIdsFromScope, repeatDatasetKeys, screenRoutePathToUrl } from "@aglyn/aglyn/server";
61
+ import { readUsageSources, screenIdsUsingComponentDeep, screenIdsUsingLayoutDeep, isLiveUsageCandidate } from "./live-page-usage.js";
62
+ /**
63
+ * How many documents per collection the per-site scan reads.
64
+ *
65
+ * The console's publish scan uses the same number for the same reason
66
+ * (AGL-1161): this decides which caches get dropped, so a prefix scan would
67
+ * report a refreshed site and leave real pages serving the old rows. Bounded
68
+ * all the same, and when the bound bites it is SAID rather than absorbed.
69
+ */ export const DATASET_SCAN_LIMIT = 2000;
70
+ /**
71
+ * How many sites one record write fans out to.
72
+ *
73
+ * `MAX_SCOPE_HOSTS` is the ceiling on a NAMED selection, so only an org-wide
74
+ * dataset can reach this — an org with more sites than this pays a scan per
75
+ * site on every write otherwise, which is a cost the person writing one row
76
+ * did not ask for. Sites past the cap keep the TTL they had before this
77
+ * existed, and the shortfall is returned and logged rather than swallowed.
78
+ */ export const MAX_DATASET_FANOUT_HOSTS = 30;
79
+ /**
80
+ * How long one site-and-dataset pair waits before announcing again.
81
+ *
82
+ * A 1,000-row import is one announce because the importer announces after its
83
+ * loop, not per row. This covers the other burst — many small writes arriving
84
+ * separately, a form under load or an automation fanning out — where each one
85
+ * would otherwise pay a full scan per site and hand the tenant the same path
86
+ * list again.
87
+ *
88
+ * Leading edge, so the FIRST write of a burst refreshes immediately; what is
89
+ * skipped is a repeat inside the window, and the next write after it announces
90
+ * again. The cost of the skip is bounded by the window: a row written during
91
+ * one is visible no later than the next announce.
92
+ */ export const DATASET_ANNOUNCE_WINDOW_MS = 2000;
93
+ /** Last announce per `orgId/datasetId/hostId`, for the window above. */ const lastAnnouncedAt = new Map();
94
+ /**
95
+ * Bounded so a long-lived server cannot grow this map without limit. Far above
96
+ * the number of datasets any one instance writes in a window; when it bites,
97
+ * the oldest entries simply lose their throttle and announce again.
98
+ */ const MAX_THROTTLE_ENTRIES = 5000;
99
+ function throttleAllows(key, now) {
100
+ const previous = lastAnnouncedAt.get(key);
101
+ if (previous !== undefined && now - previous < DATASET_ANNOUNCE_WINDOW_MS) {
102
+ return false;
103
+ }
104
+ if (lastAnnouncedAt.size >= MAX_THROTTLE_ENTRIES) lastAnnouncedAt.clear();
105
+ lastAnnouncedAt.set(key, now);
106
+ return true;
107
+ }
108
+ /** Test seam: the window is process-wide state, so a spec has to reset it. */ export function resetDatasetAnnounceThrottle() {
109
+ lastAnnouncedAt.clear();
110
+ }
111
+ /**
112
+ * Whether a node tree repeats over any of `keys`.
113
+ *
114
+ * Asks `repeatDatasetKeys` — literally the function the tenant composer calls
115
+ * to decide which datasets to load for a page — so this cannot come to
116
+ * disagree with what actually renders. A predicate even slightly narrower than
117
+ * the expansion's is a page that renders rows and never refreshes them.
118
+ */ function nodesRepeatDataset(nodes, keys) {
119
+ return repeatDatasetKeys(nodes).some((key)=>keys.has(key));
120
+ }
121
+ /**
122
+ * Every live screen whose rendered output repeats over the dataset, however
123
+ * indirectly.
124
+ *
125
+ * The same three-bucket closure a placed form's pages are found by, for the
126
+ * same reason: a binding is found by SEARCHING node trees rather than by
127
+ * matching a pointer, so it can sit on the screen, in the chrome of the layout
128
+ * the screen renders inside, or inside a reusable component a third component
129
+ * nests. After the first level the fan-out is identical, so it is delegated
130
+ * rather than restated.
131
+ *
132
+ * Pure, and separate from the Firestore read, so the closure is testable
133
+ * without a database.
134
+ */ export function screenIdsRepeatingDataset(keys, sources) {
135
+ if (!keys.size) return [];
136
+ const screenIds = new Set();
137
+ for (const candidate of sources.screens){
138
+ if (!isLiveUsageCandidate(candidate)) continue;
139
+ if (nodesRepeatDataset(candidate.nodes, keys)) screenIds.add(candidate.id);
140
+ }
141
+ for (const candidate of sources.layouts){
142
+ if (!isLiveUsageCandidate(candidate)) continue;
143
+ if (!nodesRepeatDataset(candidate.nodes, keys)) continue;
144
+ // The layout itself renders no URL; the screens beneath it do.
145
+ for (const screenId of screenIdsUsingLayoutDeep(candidate.id, sources.screens, sources.layouts)){
146
+ screenIds.add(screenId);
147
+ }
148
+ }
149
+ for (const candidate of sources.components){
150
+ if (!isLiveUsageCandidate(candidate)) continue;
151
+ if (!nodesRepeatDataset(candidate.nodes, keys)) continue;
152
+ // And a component renders wherever it is placed, however deeply nested.
153
+ for (const screenId of screenIdsUsingComponentDeep(candidate.id, sources)){
154
+ screenIds.add(screenId);
155
+ }
156
+ }
157
+ return [
158
+ ...screenIds
159
+ ];
160
+ }
161
+ /**
162
+ * The keys a page can be bound to this dataset by: its id, and the display
163
+ * name editors type into the Repeat attribute.
164
+ *
165
+ * Both, because the expansion looks a key up either way (`getDatasets`
166
+ * resolves an id first and then a display name). Matching only the id would
167
+ * miss every binding written by name, which is the older and still-supported
168
+ * half of them.
169
+ */ function datasetKeysOf(snapshot) {
170
+ var _snapshot_get;
171
+ const keys = new Set([
172
+ snapshot.id
173
+ ]);
174
+ const displayName = String((_snapshot_get = snapshot.get('displayName')) != null ? _snapshot_get : '').trim();
175
+ if (displayName) keys.add(displayName);
176
+ return keys;
177
+ }
178
+ /**
179
+ * The sites a dataset is shared with, as host ids.
180
+ *
181
+ * Fails CLOSED on a missing or empty `visibleTo`, matching `visibleToHost` and
182
+ * both enforcement layers underneath it: a document nobody scoped is shared
183
+ * with nobody, and reading it as org-wide here would announce one org's
184
+ * dataset change to sites that cannot see the dataset at all.
185
+ */ async function hostIdsSharingDataset(firestore, orgId, visibleTo) {
186
+ if (!Array.isArray(visibleTo) || !visibleTo.length) {
187
+ return {
188
+ hostIds: [],
189
+ hostsDropped: 0
190
+ };
191
+ }
192
+ if (!visibleTo.includes(ORG_SCOPE_TOKEN)) {
193
+ const named = hostIdsFromScope(visibleTo);
194
+ return {
195
+ hostIds: named,
196
+ hostsDropped: 0
197
+ };
198
+ }
199
+ // One over the cap, so a shortfall is detected rather than assumed away.
200
+ const hosts = await firestore.collection('hosts').where('orgId', '==', orgId).limit(MAX_DATASET_FANOUT_HOSTS + 1).get();
201
+ const all = hosts.docs.map((doc)=>doc.id);
202
+ return {
203
+ hostIds: all.slice(0, MAX_DATASET_FANOUT_HOSTS),
204
+ hostsDropped: Math.max(0, all.length - MAX_DATASET_FANOUT_HOSTS)
205
+ };
206
+ }
207
+ /** One site's paths, or `null` when the site has nothing to refresh. */ async function targetForHost(firestore, hostId, keys) {
208
+ var _hostSnapshot_get, _hostSnapshot_get1, _hostSnapshot_get2;
209
+ const hostRef = firestore.collection('hosts').doc(hostId);
210
+ const hostSnapshot = await hostRef.get();
211
+ if (!hostSnapshot.exists) return null;
212
+ const subdomain = String((_hostSnapshot_get = hostSnapshot.get('subdomain')) != null ? _hostSnapshot_get : '');
213
+ // Without a subdomain there is no tenant deployment holding cached pages for
214
+ // this site — the ordinary state of one not yet given a name, not a failure.
215
+ if (!subdomain) return null;
216
+ const sources = await readUsageSources(hostRef, DATASET_SCAN_LIMIT);
217
+ const screens = (_hostSnapshot_get1 = hostSnapshot.get('screens')) != null ? _hostSnapshot_get1 : {};
218
+ const paths = screenIdsRepeatingDataset(keys, sources.candidates).map((screenId)=>screens[screenId]).filter((path)=>Boolean(path)).map((path)=>screenRoutePathToUrl(path)).filter((path, index, all)=>all.indexOf(path) === index);
219
+ if (!paths.length) return null;
220
+ const cname = String((_hostSnapshot_get2 = hostSnapshot.get('cname')) != null ? _hostSnapshot_get2 : '');
221
+ return _extends({
222
+ hostId,
223
+ subdomain
224
+ }, cname ? {
225
+ cname
226
+ } : {}, {
227
+ paths,
228
+ truncated: sources.truncated
229
+ });
230
+ }
231
+ /**
232
+ * Every live page a change to this dataset's records makes stale, per site.
233
+ *
234
+ * Reads only; announcing is the caller's, because the two runtimes announce
235
+ * differently — the console posts to the tenant over the shared secret, and
236
+ * the tenant drops its own caches in process. Splitting it here is what lets
237
+ * both use one answer to the question of WHICH pages.
238
+ *
239
+ * Never throws. A scope that could not be computed is a scope of nothing, and
240
+ * the TTL underneath is unaffected.
241
+ */ export async function datasetLivePageScope(options) {
242
+ const { firestore, orgId, datasetId } = options;
243
+ if (!orgId || !datasetId) {
244
+ return {
245
+ targets: [],
246
+ hostsDropped: 0,
247
+ reason: 'no-dataset'
248
+ };
249
+ }
250
+ try {
251
+ const snapshot = await firestore.collection('orgs').doc(orgId).collection('datasets').doc(datasetId).get();
252
+ if (!snapshot.exists) {
253
+ return {
254
+ targets: [],
255
+ hostsDropped: 0,
256
+ reason: 'no-dataset'
257
+ };
258
+ }
259
+ const { hostIds, hostsDropped } = await hostIdsSharingDataset(firestore, orgId, snapshot.get('visibleTo'));
260
+ if (!hostIds.length) {
261
+ return {
262
+ targets: [],
263
+ hostsDropped,
264
+ reason: 'no-sites'
265
+ };
266
+ }
267
+ const keys = datasetKeysOf(snapshot);
268
+ const resolved = await Promise.all(hostIds.map(async (hostId)=>{
269
+ try {
270
+ return await targetForHost(firestore, hostId, keys);
271
+ } catch (error) {
272
+ // One unreadable site must not cost the others their refresh.
273
+ console.error('[dataset-live-pages] site scan failed', hostId, error);
274
+ return null;
275
+ }
276
+ }));
277
+ const targets = resolved.filter((target)=>target !== null);
278
+ return {
279
+ targets,
280
+ hostsDropped,
281
+ reason: targets.length ? 'ok' : 'not-rendered'
282
+ };
283
+ } catch (error) {
284
+ console.error('[dataset-live-pages] scope failed', datasetId, error);
285
+ return {
286
+ targets: [],
287
+ hostsDropped: 0,
288
+ reason: 'error'
289
+ };
290
+ }
291
+ }
292
+ let registeredDropper;
293
+ /**
294
+ * Registers the dropper for this process, replacing any previous one. Returns
295
+ * the unregister, which removes it only if it is still the one registered.
296
+ *
297
+ * Called from a function the platform calls BY NAME at boot, never from a
298
+ * module's top level: a bundler deletes a module imported only for its side
299
+ * effect when its package says it has none (AGL-3025).
300
+ */ export function registerLivePageDropper(drop) {
301
+ registeredDropper = drop;
302
+ return ()=>{
303
+ if (registeredDropper === drop) registeredDropper = undefined;
304
+ };
305
+ }
306
+ const EMPTY_RESULT = {
307
+ sitesRefreshed: 0,
308
+ pathsRefreshed: 0,
309
+ sitesRefused: 0,
310
+ hostsDropped: 0,
311
+ scanTruncated: false
312
+ };
313
+ /**
314
+ * Refresh every live page that shows this dataset's records.
315
+ *
316
+ * The one call a write path makes. BEST EFFORT, ALWAYS: it never throws and
317
+ * never rejects, so a caller can `await` it inside the try that already guards
318
+ * the write without any chance of turning a stored record into a failed one.
319
+ *
320
+ * `drop` overrides the registered dropper for callers that hold their own —
321
+ * the console, whose announce is an authenticated request rather than a local
322
+ * cache call.
323
+ */ export async function announceDatasetRecordChange(options) {
324
+ var _options_drop;
325
+ const { firestore, orgId, datasetId } = options;
326
+ const drop = (_options_drop = options.drop) != null ? _options_drop : registeredDropper;
327
+ if (!drop) {
328
+ // Said out loud: a process that writes records and registered no dropper
329
+ // is one where this feature is silently absent, which reads exactly like
330
+ // a working one until somebody watches a page fail to change.
331
+ console.warn(JSON.stringify({
332
+ tag: 'AGL-3113:no-live-page-dropper',
333
+ orgId,
334
+ datasetId
335
+ }));
336
+ return _extends({}, EMPTY_RESULT, {
337
+ reason: 'no-dropper'
338
+ });
339
+ }
340
+ const throttleKey = `${orgId}/${datasetId}`;
341
+ if (!throttleAllows(throttleKey, Date.now())) {
342
+ return _extends({}, EMPTY_RESULT, {
343
+ reason: 'throttled'
344
+ });
345
+ }
346
+ const scope = await datasetLivePageScope({
347
+ firestore,
348
+ orgId,
349
+ datasetId
350
+ });
351
+ if (!scope.targets.length) {
352
+ return _extends({}, EMPTY_RESULT, {
353
+ hostsDropped: scope.hostsDropped,
354
+ reason: scope.reason
355
+ });
356
+ }
357
+ let sitesRefreshed = 0;
358
+ let sitesRefused = 0;
359
+ let pathsRefreshed = 0;
360
+ for (const target of scope.targets){
361
+ let landed = false;
362
+ try {
363
+ landed = await drop(target);
364
+ } catch (error) {
365
+ // A dropper that throws is a dropper that failed; it is not a write
366
+ // that failed.
367
+ console.error('[dataset-live-pages] drop failed', target.hostId, error);
368
+ }
369
+ if (landed) {
370
+ sitesRefreshed += 1;
371
+ pathsRefreshed += target.paths.length;
372
+ } else {
373
+ sitesRefused += 1;
374
+ }
375
+ }
376
+ const scanTruncated = scope.targets.some((target)=>target.truncated);
377
+ const result = {
378
+ sitesRefreshed,
379
+ pathsRefreshed,
380
+ sitesRefused,
381
+ hostsDropped: scope.hostsDropped,
382
+ scanTruncated,
383
+ reason: scope.reason
384
+ };
385
+ // ONE line per announce, on success as well as failure, for the reason
386
+ // `postTenantRevalidate` gives: a record that only logs its failures cannot
387
+ // tell "every write refreshed" apart from "the call never happened", and
388
+ // that ambiguity is how an eleven-day propagation outage went unnoticed.
389
+ console.log(JSON.stringify(_extends({
390
+ tag: 'AGL-3113:dataset-announce',
391
+ orgId,
392
+ datasetId
393
+ }, result)));
394
+ return result;
395
+ }
396
+ /**
397
+ * One sentence for a refresh that did not cover every page, or `null` when it
398
+ * did.
399
+ *
400
+ * Beside the call rather than at each surface, so the console cannot word this
401
+ * differently in two places — the same reason `describeRevalidateShortfall`
402
+ * lives beside `revalidateLivePages`. `lead` names the act that succeeded,
403
+ * because only that word differs between surfaces: a record is SAVED, and
404
+ * telling someone who fixed a typo that they published it is a worse error
405
+ * than the staleness the sentence is about.
406
+ */ export function describeDatasetAnnounceShortfall(result, lead = 'Saved.') {
407
+ if (!result) return null;
408
+ // `not-rendered`, `no-sites` and `throttled` are deliberately silent: a
409
+ // dataset no page shows has no stale page, and a throttled drop means one
410
+ // already went out a moment ago. Saying anything would be alarming and wrong.
411
+ if (result.reason === 'error' || result.sitesRefused) {
412
+ return `${lead} The live pages could not be refreshed just now, so they ` + 'may show the previous records for up to an hour.';
413
+ }
414
+ if (result.hostsDropped || result.scanTruncated) {
415
+ return `${lead} Some pages that use this dataset are too many to refresh at ` + 'once — they update on their own within an hour.';
416
+ }
417
+ return null;
418
+ }
419
+ export default announceDatasetRecordChange;
420
+
421
+ //# sourceMappingURL=dataset-live-pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/dataset-live-pages.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 * A dataset record write refreshes the live pages that show it (AGL-3113).\n *\n * ## What was missing\n *\n * Every other change a visitor can see announces itself the moment it lands: a\n * publish, a layout, a component, a content entry, a redirect rule, the\n * maintenance toggle. A dataset RECORD did not. It was written — by a form, by\n * an automation, over `/v1`, from the console — and the pages repeating over\n * it went on serving the rows they were built from until the hour-long window\n * lapsed. `PUBLISHED_SITE_DATA_TTL_SECONDS` was the propagation mechanism,\n * which is the same mistake AGL-1302 and AGL-1150 each had to undo elsewhere:\n * a backstop doing a job nothing else was doing.\n *\n * ## Scope, not the site\n *\n * A record changed one dataset, so this drops the pages that repeat over that\n * dataset and nothing else. The set is derived from the SAME binding the\n * published page expands — `repeatDatasetKeys`, the generic\n * `props.repeatDataset` any node can now carry (AGL-3111) — rather than from a\n * list of components that may repeat. A component allowlist was already wrong\n * the day repeat stopped being the Stack's: a heading bound to a dataset\n * renders rows exactly as a Stack does, and would have been missed.\n *\n * Three corpora, because the composer reads repeat keys after the layout chain\n * and the reusable-component graft: a repeat in a layout's chrome or inside a\n * component reaches every screen that renders it.\n *\n * ## Which sites\n *\n * Datasets are ORG-scoped and shared with sites through `visibleTo` — every\n * site in the org, or up to `MAX_SCOPE_HOSTS` named ones. Each site caches its\n * own copy of the rows under its own `tenant-data:{hostId}` tag and its own\n * page keys, so one record write is a fan-out. A missing `visibleTo` is\n * visible to NOBODY and announces nothing; defaulting it to org-wide here\n * would re-make AGL-1466 in the cache layer.\n *\n * ## Best effort, always\n *\n * The record is already written by the time any of this runs. A refused or\n * failed drop is reported to the caller and logged, never thrown: a record\n * must not be lost because a cache refused, and the TTL is still underneath as\n * the backstop it was always meant to be.\n */\n\nimport {\n ORG_SCOPE_TOKEN,\n hostIdsFromScope,\n repeatDatasetKeys,\n screenRoutePathToUrl,\n} from '@aglyn/aglyn/server'\nimport type { Firestore } from 'firebase-admin/firestore'\nimport {\n readUsageSources,\n screenIdsUsingComponentDeep,\n screenIdsUsingLayoutDeep,\n isLiveUsageCandidate,\n type UsageCandidate,\n type UsageSources,\n} from './live-page-usage'\n\n/**\n * How many documents per collection the per-site scan reads.\n *\n * The console's publish scan uses the same number for the same reason\n * (AGL-1161): this decides which caches get dropped, so a prefix scan would\n * report a refreshed site and leave real pages serving the old rows. Bounded\n * all the same, and when the bound bites it is SAID rather than absorbed.\n */\nexport const DATASET_SCAN_LIMIT = 2000\n\n/**\n * How many sites one record write fans out to.\n *\n * `MAX_SCOPE_HOSTS` is the ceiling on a NAMED selection, so only an org-wide\n * dataset can reach this — an org with more sites than this pays a scan per\n * site on every write otherwise, which is a cost the person writing one row\n * did not ask for. Sites past the cap keep the TTL they had before this\n * existed, and the shortfall is returned and logged rather than swallowed.\n */\nexport const MAX_DATASET_FANOUT_HOSTS = 30\n\n/**\n * How long one site-and-dataset pair waits before announcing again.\n *\n * A 1,000-row import is one announce because the importer announces after its\n * loop, not per row. This covers the other burst — many small writes arriving\n * separately, a form under load or an automation fanning out — where each one\n * would otherwise pay a full scan per site and hand the tenant the same path\n * list again.\n *\n * Leading edge, so the FIRST write of a burst refreshes immediately; what is\n * skipped is a repeat inside the window, and the next write after it announces\n * again. The cost of the skip is bounded by the window: a row written during\n * one is visible no later than the next announce.\n */\nexport const DATASET_ANNOUNCE_WINDOW_MS = 2000\n\n/** Last announce per `orgId/datasetId/hostId`, for the window above. */\nconst lastAnnouncedAt = new Map<string, number>()\n\n/**\n * Bounded so a long-lived server cannot grow this map without limit. Far above\n * the number of datasets any one instance writes in a window; when it bites,\n * the oldest entries simply lose their throttle and announce again.\n */\nconst MAX_THROTTLE_ENTRIES = 5000\n\nfunction throttleAllows(key: string, now: number): boolean {\n const previous = lastAnnouncedAt.get(key)\n if (previous !== undefined && now - previous < DATASET_ANNOUNCE_WINDOW_MS) {\n return false\n }\n if (lastAnnouncedAt.size >= MAX_THROTTLE_ENTRIES) lastAnnouncedAt.clear()\n lastAnnouncedAt.set(key, now)\n return true\n}\n\n/** Test seam: the window is process-wide state, so a spec has to reset it. */\nexport function resetDatasetAnnounceThrottle(): void {\n lastAnnouncedAt.clear()\n}\n\n/** One site's share of a dataset change. */\nexport interface DatasetLivePageTarget {\n hostId: string\n /** The tenant keys its cache on this, never on `hostId`. */\n subdomain: string\n /** The attached custom domain, whose pages live under a second cache key. */\n cname?: string\n /** Site-absolute addresses (`/`, `/team`) that repeat over the dataset. */\n paths: string[]\n /** The site held more documents than the scan read, so `paths` is partial. */\n truncated: boolean\n}\n\nexport interface DatasetLivePageScope {\n targets: DatasetLivePageTarget[]\n /**\n * Sites the fan-out cap left out. Zero in the ordinary case, and reported\n * rather than logged because a caller that says \"refreshed\" while some sites\n * were skipped is the failure this whole arc exists to remove.\n */\n hostsDropped: number\n /**\n * Why there is nothing to announce, or `'ok'`.\n *\n * `'not-rendered'` is a success: a dataset no page repeats over has no live\n * page to refresh. It is kept apart from `'error'` for the same reason the\n * revalidate route keeps `not-routed` apart from a refusal — one means there\n * was nothing to do, the other means something was left stale.\n */\n reason: 'ok' | 'no-dataset' | 'no-sites' | 'not-rendered' | 'error'\n}\n\n/**\n * Whether a node tree repeats over any of `keys`.\n *\n * Asks `repeatDatasetKeys` — literally the function the tenant composer calls\n * to decide which datasets to load for a page — so this cannot come to\n * disagree with what actually renders. A predicate even slightly narrower than\n * the expansion's is a page that renders rows and never refreshes them.\n */\nfunction nodesRepeatDataset(\n nodes: UsageCandidate['nodes'],\n keys: ReadonlySet<string>,\n): boolean {\n return repeatDatasetKeys(nodes).some((key) => keys.has(key))\n}\n\n/**\n * Every live screen whose rendered output repeats over the dataset, however\n * indirectly.\n *\n * The same three-bucket closure a placed form's pages are found by, for the\n * same reason: a binding is found by SEARCHING node trees rather than by\n * matching a pointer, so it can sit on the screen, in the chrome of the layout\n * the screen renders inside, or inside a reusable component a third component\n * nests. After the first level the fan-out is identical, so it is delegated\n * rather than restated.\n *\n * Pure, and separate from the Firestore read, so the closure is testable\n * without a database.\n */\nexport function screenIdsRepeatingDataset(\n keys: ReadonlySet<string>,\n sources: UsageSources,\n): string[] {\n if (!keys.size) return []\n const screenIds = new Set<string>()\n\n for (const candidate of sources.screens) {\n if (!isLiveUsageCandidate(candidate)) continue\n if (nodesRepeatDataset(candidate.nodes, keys)) screenIds.add(candidate.id)\n }\n for (const candidate of sources.layouts) {\n if (!isLiveUsageCandidate(candidate)) continue\n if (!nodesRepeatDataset(candidate.nodes, keys)) continue\n // The layout itself renders no URL; the screens beneath it do.\n for (const screenId of screenIdsUsingLayoutDeep(\n candidate.id,\n sources.screens,\n sources.layouts,\n )) {\n screenIds.add(screenId)\n }\n }\n for (const candidate of sources.components) {\n if (!isLiveUsageCandidate(candidate)) continue\n if (!nodesRepeatDataset(candidate.nodes, keys)) continue\n // And a component renders wherever it is placed, however deeply nested.\n for (const screenId of screenIdsUsingComponentDeep(candidate.id, sources)) {\n screenIds.add(screenId)\n }\n }\n\n return [...screenIds]\n}\n\n/**\n * The keys a page can be bound to this dataset by: its id, and the display\n * name editors type into the Repeat attribute.\n *\n * Both, because the expansion looks a key up either way (`getDatasets`\n * resolves an id first and then a display name). Matching only the id would\n * miss every binding written by name, which is the older and still-supported\n * half of them.\n */\nfunction datasetKeysOf(\n snapshot: FirebaseFirestore.DocumentSnapshot,\n): Set<string> {\n const keys = new Set<string>([snapshot.id])\n const displayName = String(snapshot.get('displayName') ?? '').trim()\n if (displayName) keys.add(displayName)\n return keys\n}\n\n/**\n * The sites a dataset is shared with, as host ids.\n *\n * Fails CLOSED on a missing or empty `visibleTo`, matching `visibleToHost` and\n * both enforcement layers underneath it: a document nobody scoped is shared\n * with nobody, and reading it as org-wide here would announce one org's\n * dataset change to sites that cannot see the dataset at all.\n */\nasync function hostIdsSharingDataset(\n firestore: Firestore,\n orgId: string,\n visibleTo: readonly string[] | undefined,\n): Promise<{ hostIds: string[]; hostsDropped: number }> {\n if (!Array.isArray(visibleTo) || !visibleTo.length) {\n return { hostIds: [], hostsDropped: 0 }\n }\n if (!visibleTo.includes(ORG_SCOPE_TOKEN)) {\n const named = hostIdsFromScope(visibleTo)\n return { hostIds: named, hostsDropped: 0 }\n }\n // One over the cap, so a shortfall is detected rather than assumed away.\n const hosts = await firestore\n .collection('hosts')\n .where('orgId', '==', orgId)\n .limit(MAX_DATASET_FANOUT_HOSTS + 1)\n .get()\n const all = hosts.docs.map((doc) => doc.id)\n return {\n hostIds: all.slice(0, MAX_DATASET_FANOUT_HOSTS),\n hostsDropped: Math.max(0, all.length - MAX_DATASET_FANOUT_HOSTS),\n }\n}\n\n/** One site's paths, or `null` when the site has nothing to refresh. */\nasync function targetForHost(\n firestore: Firestore,\n hostId: string,\n keys: ReadonlySet<string>,\n): Promise<DatasetLivePageTarget | null> {\n const hostRef = firestore.collection('hosts').doc(hostId)\n const hostSnapshot = await hostRef.get()\n if (!hostSnapshot.exists) return null\n const subdomain = String(hostSnapshot.get('subdomain') ?? '')\n // Without a subdomain there is no tenant deployment holding cached pages for\n // this site — the ordinary state of one not yet given a name, not a failure.\n if (!subdomain) return null\n\n const sources = await readUsageSources(hostRef, DATASET_SCAN_LIMIT)\n const screens = (hostSnapshot.get('screens') ?? {}) as Record<string, string>\n const paths = screenIdsRepeatingDataset(keys, sources.candidates)\n .map((screenId) => screens[screenId])\n .filter((path): path is string => Boolean(path))\n .map((path) => screenRoutePathToUrl(path))\n .filter((path, index, all) => all.indexOf(path) === index)\n if (!paths.length) return null\n\n const cname = String(hostSnapshot.get('cname') ?? '')\n return {\n hostId,\n subdomain,\n ...(cname ? { cname } : {}),\n paths,\n truncated: sources.truncated,\n }\n}\n\n/**\n * Every live page a change to this dataset's records makes stale, per site.\n *\n * Reads only; announcing is the caller's, because the two runtimes announce\n * differently — the console posts to the tenant over the shared secret, and\n * the tenant drops its own caches in process. Splitting it here is what lets\n * both use one answer to the question of WHICH pages.\n *\n * Never throws. A scope that could not be computed is a scope of nothing, and\n * the TTL underneath is unaffected.\n */\nexport async function datasetLivePageScope(options: {\n firestore: Firestore\n orgId: string\n datasetId: string\n}): Promise<DatasetLivePageScope> {\n const { firestore, orgId, datasetId } = options\n if (!orgId || !datasetId) {\n return { targets: [], hostsDropped: 0, reason: 'no-dataset' }\n }\n try {\n const snapshot = await firestore\n .collection('orgs')\n .doc(orgId)\n .collection('datasets')\n .doc(datasetId)\n .get()\n if (!snapshot.exists) {\n return { targets: [], hostsDropped: 0, reason: 'no-dataset' }\n }\n const { hostIds, hostsDropped } = await hostIdsSharingDataset(\n firestore,\n orgId,\n snapshot.get('visibleTo') as readonly string[] | undefined,\n )\n if (!hostIds.length) {\n return { targets: [], hostsDropped, reason: 'no-sites' }\n }\n const keys = datasetKeysOf(snapshot)\n const resolved = await Promise.all(\n hostIds.map(async (hostId) => {\n try {\n return await targetForHost(firestore, hostId, keys)\n } catch (error) {\n // One unreadable site must not cost the others their refresh.\n console.error('[dataset-live-pages] site scan failed', hostId, error)\n return null\n }\n }),\n )\n const targets = resolved.filter(\n (target): target is DatasetLivePageTarget => target !== null,\n )\n return {\n targets,\n hostsDropped,\n reason: targets.length ? 'ok' : 'not-rendered',\n }\n } catch (error) {\n console.error('[dataset-live-pages] scope failed', datasetId, error)\n return { targets: [], hostsDropped: 0, reason: 'error' }\n }\n}\n\n/**\n * How one runtime drops a site's cached pages.\n *\n * The generic seam, registered by the deployment that owns the caches. The\n * tenant registers an in-process drop at boot; the console announces over the\n * shared secret and passes its own dropper at the call site. Core names\n * neither, exactly as it names no repeat source.\n *\n * Returns whether the drop landed. It must never throw: everything above it\n * has already been written.\n */\nexport type LivePageDropper = (\n target: DatasetLivePageTarget,\n) => Promise<boolean>\n\nlet registeredDropper: LivePageDropper | undefined\n\n/**\n * Registers the dropper for this process, replacing any previous one. Returns\n * the unregister, which removes it only if it is still the one registered.\n *\n * Called from a function the platform calls BY NAME at boot, never from a\n * module's top level: a bundler deletes a module imported only for its side\n * effect when its package says it has none (AGL-3025).\n */\nexport function registerLivePageDropper(drop: LivePageDropper): () => void {\n registeredDropper = drop\n return () => {\n if (registeredDropper === drop) registeredDropper = undefined\n }\n}\n\nexport interface DatasetAnnounceResult {\n /** Sites whose cached pages were dropped. */\n sitesRefreshed: number\n /** Pages named across those sites. */\n pathsRefreshed: number\n /** Sites the drop was attempted on and refused. */\n sitesRefused: number\n /** Sites past the fan-out cap, and sites whose scan was truncated. */\n hostsDropped: number\n scanTruncated: boolean\n /** `'throttled'` when a drop for this pair already went out just now. */\n reason: DatasetLivePageScope['reason'] | 'throttled' | 'no-dropper'\n}\n\nconst EMPTY_RESULT: Omit<DatasetAnnounceResult, 'reason'> = {\n sitesRefreshed: 0,\n pathsRefreshed: 0,\n sitesRefused: 0,\n hostsDropped: 0,\n scanTruncated: false,\n}\n\n/**\n * Refresh every live page that shows this dataset's records.\n *\n * The one call a write path makes. BEST EFFORT, ALWAYS: it never throws and\n * never rejects, so a caller can `await` it inside the try that already guards\n * the write without any chance of turning a stored record into a failed one.\n *\n * `drop` overrides the registered dropper for callers that hold their own —\n * the console, whose announce is an authenticated request rather than a local\n * cache call.\n */\nexport async function announceDatasetRecordChange(options: {\n firestore: Firestore\n orgId: string\n datasetId: string\n drop?: LivePageDropper\n}): Promise<DatasetAnnounceResult> {\n const { firestore, orgId, datasetId } = options\n const drop = options.drop ?? registeredDropper\n if (!drop) {\n // Said out loud: a process that writes records and registered no dropper\n // is one where this feature is silently absent, which reads exactly like\n // a working one until somebody watches a page fail to change.\n console.warn(\n JSON.stringify({\n tag: 'AGL-3113:no-live-page-dropper',\n orgId,\n datasetId,\n }),\n )\n return { ...EMPTY_RESULT, reason: 'no-dropper' }\n }\n const throttleKey = `${orgId}/${datasetId}`\n if (!throttleAllows(throttleKey, Date.now())) {\n return { ...EMPTY_RESULT, reason: 'throttled' }\n }\n const scope = await datasetLivePageScope({ firestore, orgId, datasetId })\n if (!scope.targets.length) {\n return {\n ...EMPTY_RESULT,\n hostsDropped: scope.hostsDropped,\n reason: scope.reason,\n }\n }\n\n let sitesRefreshed = 0\n let sitesRefused = 0\n let pathsRefreshed = 0\n for (const target of scope.targets) {\n let landed = false\n try {\n landed = await drop(target)\n } catch (error) {\n // A dropper that throws is a dropper that failed; it is not a write\n // that failed.\n console.error('[dataset-live-pages] drop failed', target.hostId, error)\n }\n if (landed) {\n sitesRefreshed += 1\n pathsRefreshed += target.paths.length\n } else {\n sitesRefused += 1\n }\n }\n\n const scanTruncated = scope.targets.some((target) => target.truncated)\n const result: DatasetAnnounceResult = {\n sitesRefreshed,\n pathsRefreshed,\n sitesRefused,\n hostsDropped: scope.hostsDropped,\n scanTruncated,\n reason: scope.reason,\n }\n // ONE line per announce, on success as well as failure, for the reason\n // `postTenantRevalidate` gives: a record that only logs its failures cannot\n // tell \"every write refreshed\" apart from \"the call never happened\", and\n // that ambiguity is how an eleven-day propagation outage went unnoticed.\n console.log(\n JSON.stringify({\n tag: 'AGL-3113:dataset-announce',\n orgId,\n datasetId,\n ...result,\n }),\n )\n return result\n}\n\n/**\n * One sentence for a refresh that did not cover every page, or `null` when it\n * did.\n *\n * Beside the call rather than at each surface, so the console cannot word this\n * differently in two places — the same reason `describeRevalidateShortfall`\n * lives beside `revalidateLivePages`. `lead` names the act that succeeded,\n * because only that word differs between surfaces: a record is SAVED, and\n * telling someone who fixed a typo that they published it is a worse error\n * than the staleness the sentence is about.\n */\nexport function describeDatasetAnnounceShortfall(\n result: DatasetAnnounceResult | null,\n lead = 'Saved.',\n): string | null {\n if (!result) return null\n // `not-rendered`, `no-sites` and `throttled` are deliberately silent: a\n // dataset no page shows has no stale page, and a throttled drop means one\n // already went out a moment ago. Saying anything would be alarming and wrong.\n if (result.reason === 'error' || result.sitesRefused) {\n return (\n `${lead} The live pages could not be refreshed just now, so they ` +\n 'may show the previous records for up to an hour.'\n )\n }\n if (result.hostsDropped || result.scanTruncated) {\n return (\n `${lead} Some pages that use this dataset are too many to refresh at ` +\n 'once — they update on their own within an hour.'\n )\n }\n return null\n}\n\nexport default announceDatasetRecordChange\n"],"names":["ORG_SCOPE_TOKEN","hostIdsFromScope","repeatDatasetKeys","screenRoutePathToUrl","readUsageSources","screenIdsUsingComponentDeep","screenIdsUsingLayoutDeep","isLiveUsageCandidate","DATASET_SCAN_LIMIT","MAX_DATASET_FANOUT_HOSTS","DATASET_ANNOUNCE_WINDOW_MS","lastAnnouncedAt","Map","MAX_THROTTLE_ENTRIES","throttleAllows","key","now","previous","get","undefined","size","clear","set","resetDatasetAnnounceThrottle","nodesRepeatDataset","nodes","keys","some","has","screenIdsRepeatingDataset","sources","screenIds","Set","candidate","screens","add","id","layouts","screenId","components","datasetKeysOf","snapshot","displayName","String","trim","hostIdsSharingDataset","firestore","orgId","visibleTo","Array","isArray","length","hostIds","hostsDropped","includes","named","hosts","collection","where","limit","all","docs","map","doc","slice","Math","max","targetForHost","hostId","hostSnapshot","hostRef","exists","subdomain","paths","candidates","filter","path","Boolean","index","indexOf","cname","truncated","datasetLivePageScope","options","datasetId","targets","reason","resolved","Promise","error","console","target","registeredDropper","registerLivePageDropper","drop","EMPTY_RESULT","sitesRefreshed","pathsRefreshed","sitesRefused","scanTruncated","announceDatasetRecordChange","warn","JSON","stringify","tag","throttleKey","Date","scope","landed","result","log","describeDatasetAnnounceShortfall","lead"],"mappings":"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"}
@@ -0,0 +1,53 @@
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
+ /** Resolvers asked before the runtime's own. */
18
+ export declare const PUBLIC_DNS_RESOLVERS: string[];
19
+ /** Whether a resolver error code is an answer rather than a failure to get one. */
20
+ export declare function isConclusiveDnsCode(code: string | undefined): boolean;
21
+ export interface DnsLookupResult<T> {
22
+ /**
23
+ * True when the lookup got an answer, including the answer "there is
24
+ * nothing here". False only when nobody answered.
25
+ */
26
+ answered: boolean;
27
+ /** Empty on a conclusive miss and on an unreachable lookup alike. */
28
+ records: T[];
29
+ }
30
+ /**
31
+ * TXT records at `host`, each joined from its chunks and trimmed.
32
+ *
33
+ * A TXT answer arrives as an array of string CHUNKS per record, because DNS
34
+ * splits strings longer than 255 bytes. Comparing without joining means a long
35
+ * value — every DKIM public key, for instance — can never match.
36
+ */
37
+ export declare function lookupTxt(host: string): Promise<DnsLookupResult<string>>;
38
+ /**
39
+ * CAA records at `host`, as `<flags> <tag> "<value>"` strings.
40
+ *
41
+ * NOT walked up the tree. The caller asks about one exact name, because the
42
+ * question this exists for is "does THIS name publish a policy of its own",
43
+ * and a walk would answer a different one — a parent's policy is inherited by
44
+ * resolution, not by publication, and reporting the parent's records as the
45
+ * child's would say a name restricts issuance when it publishes nothing.
46
+ */
47
+ export declare function lookupCaa(host: string): Promise<DnsLookupResult<string>>;
48
+ export interface MxRecord {
49
+ exchange: string;
50
+ priority: number;
51
+ }
52
+ /** MX records at `host`, exchanges lowercased and stripped of a trailing dot. */
53
+ export declare function lookupMx(host: string): Promise<DnsLookupResult<MxRecord>>;