@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,131 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * The lockdown gate for VISITOR writes on a customer's live site
19
+ * (AGL-1511) — form submissions, cart changes, checkout.
20
+ *
21
+ * Why this is its own chokepoint rather than the console's. The tenant
22
+ * middleware, which is where AGL-1501 enforces, **does not match `/api`**
23
+ * (see the matcher in apps/tenant/middleware.ts): it exists to stop CACHED
24
+ * PAGES serving. So the site's own write endpoints have had no lockdown
25
+ * enforcement at all — under a full org takedown the pages 503 while a form
26
+ * POST still writes. This closes that for both modes.
27
+ *
28
+ * The product decision this file encodes, from the issue: a visitor-facing
29
+ * write on a read-serving site gets a **polite inline pause, never a
30
+ * page-level 503**. A customer's site staying up and earning is the entire
31
+ * reason read-only mode exists instead of full lockdown, and answering their
32
+ * visitors with an outage page would spend exactly what the mode was
33
+ * protecting. So the refusal is a 423 with visitor copy the block renders
34
+ * inline, and the page around it never stops rendering.
35
+ *
36
+ * Under a FULL lock the same 423 is returned rather than a 503, and that is
37
+ * deliberate too: the middleware has already replaced the whole page for
38
+ * anyone loading it, so a write arriving here is a stale tab or a script,
39
+ * and a machine-readable refusal is more useful to both than an HTML page.
40
+ *
41
+ * NO staff bypass and NO user scope, matching /api/lockdown-verdict: this is
42
+ * a public website, the caller is anonymous, and a visitor's identity plays
43
+ * no part in whether a site accepts an order.
44
+ */
45
+ import { type LockdownIntent, type LockdownPausedSurface, type LockdownState } from '@aglyn/aglyn/server';
46
+ /**
47
+ * The active platform/org/host lockdown for one site, or null.
48
+ *
49
+ * Both reads are already paid by the callers on this path — the dispatcher
50
+ * reads the host doc for its plugin deny-list and the org doc for
51
+ * enablement, and both getters are `React.cache`-deduped per request — so
52
+ * the gate costs one TTL-cached platform read and nothing else.
53
+ *
54
+ * FAIL OPEN on any error, matching every other lockdown reader: an
55
+ * unreachable Firestore is an outage, not a lockdown, and a shop that stops
56
+ * taking orders because a read timed out is a worse failure than the one
57
+ * being guarded against.
58
+ *
59
+ * …EXCEPT for a takedown (AGL-1881, extending AGL-1621). One class of lock
60
+ * inverts that cost function — "the database was down" is not an answer to a
61
+ * court order — and until now the ledger that carries it covered only the
62
+ * four scopes whose lock lives in `lockdowns/*`. Org and host locks live on
63
+ * the org/host document, so the two scopes MOST likely to be carrying a
64
+ * legal order were the two that fell through. Every successful read now
65
+ * records its org and host verdict, and a failed read serves back whichever
66
+ * of them was a takedown and is still active.
67
+ *
68
+ * Note what is NOT done here: the catch does not fail closed on the state it
69
+ * could not read. On a read failure `suspendedEnforcement` is exactly as
70
+ * unreadable as everything else, which is the whole reason a separate
71
+ * durable ledger exists — and a catch that locked unconditionally would take
72
+ * every shop on the platform offline on any transient blip, which is the
73
+ * deliberate posture this keeps for the default case.
74
+ */
75
+ export declare function getSiteLockdown(hostId: string, nowMs?: number): Promise<LockdownState | null>;
76
+ /**
77
+ * The refusal a visitor-facing write route returns, or null to proceed:
78
+ *
79
+ * const paused = await visitorWriteRefusal({ hostId, request, surface })
80
+ * if (paused) return paused
81
+ *
82
+ * `surface` picks the WORDS only (`lockdownPausedNotice`) — the checkout
83
+ * sentence has to promise no card was charged, and no generic copy can.
84
+ */
85
+ export declare function visitorWriteRefusal(options: {
86
+ hostId: string;
87
+ /** The request whose method decides read vs write. */
88
+ request?: {
89
+ method?: string;
90
+ } | null;
91
+ /** Overrides `request`; `write` when neither is given. */
92
+ intent?: LockdownIntent;
93
+ surface: LockdownPausedSurface;
94
+ nowMs?: number;
95
+ }): Promise<Response | null>;
96
+ /**
97
+ * The refusal a visitor-facing route that SERVES SITE CONTENT over `/api`
98
+ * returns under a FULL lock, or null to proceed (AGL-2495):
99
+ *
100
+ * const down = await visitorContentRefusal({ hostId })
101
+ * if (down) return down
102
+ *
103
+ * The gap this closes, found by the AGL-1621 drill. The tenant middleware
104
+ * is what takes a locked site off the air, and its matcher deliberately
105
+ * excludes `/api` — so a full org or host takedown 503s every page while an
106
+ * API route that composes and returns screen nodes keeps handing the site's
107
+ * content out to anyone who can name a screen. `visitorWriteRefusal` above
108
+ * cannot answer that: it leaves before any read, by design, because it is a
109
+ * WRITE gate on the hot unauthenticated GET path.
110
+ *
111
+ * FULL locks only, and that is the whole point of it being a separate
112
+ * helper rather than a flag on the write gate. Read-only mode exists so a
113
+ * customer's site keeps serving and earning; refusing reads there would
114
+ * spend exactly what the mode protects. `lockdownBlocks(state, 'read')` is
115
+ * true only when the mode is full, which is precisely the case where the
116
+ * pages are already gone.
117
+ *
118
+ * Deliberately NOT applied to the whole tenant API surface. A read-refusing
119
+ * gate on every route would take the lock notice, the verdict probe and the
120
+ * abuse intake down with the site. This is for the routes that serve the
121
+ * SITE ITSELF — a composed node tree — where continuing to serve is
122
+ * continuing to publish. `serve-media-cdn.ts` made the same call for bytes
123
+ * (AGL-1520); this is the node-tree half of it.
124
+ *
125
+ * Fails OPEN through `getSiteLockdown`, like every other reader here.
126
+ */
127
+ export declare function visitorContentRefusal(options: {
128
+ hostId: string;
129
+ surface?: LockdownPausedSurface;
130
+ nowMs?: number;
131
+ }): Promise<Response | null>;
@@ -0,0 +1,176 @@
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
+ * The lockdown gate for VISITOR writes on a customer's live site
18
+ * (AGL-1511) — form submissions, cart changes, checkout.
19
+ *
20
+ * Why this is its own chokepoint rather than the console's. The tenant
21
+ * middleware, which is where AGL-1501 enforces, **does not match `/api`**
22
+ * (see the matcher in apps/tenant/middleware.ts): it exists to stop CACHED
23
+ * PAGES serving. So the site's own write endpoints have had no lockdown
24
+ * enforcement at all — under a full org takedown the pages 503 while a form
25
+ * POST still writes. This closes that for both modes.
26
+ *
27
+ * The product decision this file encodes, from the issue: a visitor-facing
28
+ * write on a read-serving site gets a **polite inline pause, never a
29
+ * page-level 503**. A customer's site staying up and earning is the entire
30
+ * reason read-only mode exists instead of full lockdown, and answering their
31
+ * visitors with an outage page would spend exactly what the mode was
32
+ * protecting. So the refusal is a 423 with visitor copy the block renders
33
+ * inline, and the page around it never stops rendering.
34
+ *
35
+ * Under a FULL lock the same 423 is returned rather than a 503, and that is
36
+ * deliberate too: the middleware has already replaced the whole page for
37
+ * anyone loading it, so a write arriving here is a stale tab or a script,
38
+ * and a machine-readable refusal is more useful to both than an HTML page.
39
+ *
40
+ * NO staff bypass and NO user scope, matching /api/lockdown-verdict: this is
41
+ * a public website, the caller is anonymous, and a visitor's identity plays
42
+ * no part in whether a site accepts an order.
43
+ */ import { isLockdownActive, lockdownBlocks, lockdownIntentForMethod, lockdownPausedNotice, normalizeHostLockdown, normalizeOrgLockdown, resolveLockdown } from "@aglyn/aglyn/server";
44
+ import { getPlatformLockdown, heldSiteTakedown, lockdownJsonResponse, rememberSiteTakedown } from "./lockdown.js";
45
+ import { getHostDocAdmin, getOrgForHost } from "./organizations.js";
46
+ /**
47
+ * The active platform/org/host lockdown for one site, or null.
48
+ *
49
+ * Both reads are already paid by the callers on this path — the dispatcher
50
+ * reads the host doc for its plugin deny-list and the org doc for
51
+ * enablement, and both getters are `React.cache`-deduped per request — so
52
+ * the gate costs one TTL-cached platform read and nothing else.
53
+ *
54
+ * FAIL OPEN on any error, matching every other lockdown reader: an
55
+ * unreachable Firestore is an outage, not a lockdown, and a shop that stops
56
+ * taking orders because a read timed out is a worse failure than the one
57
+ * being guarded against.
58
+ *
59
+ * …EXCEPT for a takedown (AGL-1881, extending AGL-1621). One class of lock
60
+ * inverts that cost function — "the database was down" is not an answer to a
61
+ * court order — and until now the ledger that carries it covered only the
62
+ * four scopes whose lock lives in `lockdowns/*`. Org and host locks live on
63
+ * the org/host document, so the two scopes MOST likely to be carrying a
64
+ * legal order were the two that fell through. Every successful read now
65
+ * records its org and host verdict, and a failed read serves back whichever
66
+ * of them was a takedown and is still active.
67
+ *
68
+ * Note what is NOT done here: the catch does not fail closed on the state it
69
+ * could not read. On a read failure `suspendedEnforcement` is exactly as
70
+ * unreadable as everything else, which is the whole reason a separate
71
+ * durable ledger exists — and a catch that locked unconditionally would take
72
+ * every shop on the platform offline on any transient blip, which is the
73
+ * deliberate posture this keeps for the default case.
74
+ */ export async function getSiteLockdown(hostId, nowMs = Date.now()) {
75
+ if (!hostId) return null;
76
+ try {
77
+ const [platform, resolved, host] = await Promise.all([
78
+ getPlatformLockdown(),
79
+ getOrgForHost(hostId),
80
+ getHostDocAdmin(hostId)
81
+ ]);
82
+ const org = normalizeOrgLockdown(resolved == null ? void 0 : resolved.org);
83
+ const hostState = normalizeHostLockdown(host);
84
+ rememberSiteTakedown(hostId, {
85
+ org,
86
+ host: hostState
87
+ });
88
+ const state = resolveLockdown({
89
+ platform,
90
+ org,
91
+ host: hostState
92
+ }, nowMs);
93
+ return isLockdownActive(state, nowMs) ? state : null;
94
+ } catch (error) {
95
+ console.error('[tenant-write-lockdown] verdict failed', hostId, error);
96
+ const held = heldSiteTakedown(hostId, nowMs);
97
+ // The shipped fail-open answer, returned by the same statement it always
98
+ // was, for every case where nothing takedown-class was ever observed.
99
+ if (!held.org && !held.host) return null;
100
+ // The platform read is TTL-cached and swallows its own errors (it carries
101
+ // its own ledger entry), so asking again here costs nothing and keeps a
102
+ // wider lock from being masked by a narrower held one.
103
+ const platform = await getPlatformLockdown().catch(()=>null);
104
+ const state = resolveLockdown({
105
+ platform,
106
+ org: held.org,
107
+ host: held.host
108
+ }, nowMs);
109
+ return isLockdownActive(state, nowMs) ? state : null;
110
+ }
111
+ }
112
+ /**
113
+ * The refusal a visitor-facing write route returns, or null to proceed:
114
+ *
115
+ * const paused = await visitorWriteRefusal({ hostId, request, surface })
116
+ * if (paused) return paused
117
+ *
118
+ * `surface` picks the WORDS only (`lockdownPausedNotice`) — the checkout
119
+ * sentence has to promise no card was charged, and no generic copy can.
120
+ */ export async function visitorWriteRefusal(options) {
121
+ var _options_nowMs, _options_intent;
122
+ const nowMs = (_options_nowMs = options.nowMs) != null ? _options_nowMs : Date.now();
123
+ const intent = (_options_intent = options.intent) != null ? _options_intent : options.request ? lockdownIntentForMethod(options.request.method) : 'write';
124
+ // This is a WRITE gate: reads leave before any Firestore read, so the hot
125
+ // unauthenticated GET path pays nothing. A full lock's refusal of tenant
126
+ // API *reads* is not attempted here and is unchanged from today — the
127
+ // middleware owns the page surface, and a read-refusing API gate is its
128
+ // own decision with its own blast radius (filed separately).
129
+ if (intent === 'read') return null;
130
+ const state = await getSiteLockdown(options.hostId, nowMs);
131
+ if (!lockdownBlocks(state, intent)) return null;
132
+ return lockdownJsonResponse(state, {
133
+ notice: lockdownPausedNotice(options.surface)
134
+ });
135
+ }
136
+ /**
137
+ * The refusal a visitor-facing route that SERVES SITE CONTENT over `/api`
138
+ * returns under a FULL lock, or null to proceed (AGL-2495):
139
+ *
140
+ * const down = await visitorContentRefusal({ hostId })
141
+ * if (down) return down
142
+ *
143
+ * The gap this closes, found by the AGL-1621 drill. The tenant middleware
144
+ * is what takes a locked site off the air, and its matcher deliberately
145
+ * excludes `/api` — so a full org or host takedown 503s every page while an
146
+ * API route that composes and returns screen nodes keeps handing the site's
147
+ * content out to anyone who can name a screen. `visitorWriteRefusal` above
148
+ * cannot answer that: it leaves before any read, by design, because it is a
149
+ * WRITE gate on the hot unauthenticated GET path.
150
+ *
151
+ * FULL locks only, and that is the whole point of it being a separate
152
+ * helper rather than a flag on the write gate. Read-only mode exists so a
153
+ * customer's site keeps serving and earning; refusing reads there would
154
+ * spend exactly what the mode protects. `lockdownBlocks(state, 'read')` is
155
+ * true only when the mode is full, which is precisely the case where the
156
+ * pages are already gone.
157
+ *
158
+ * Deliberately NOT applied to the whole tenant API surface. A read-refusing
159
+ * gate on every route would take the lock notice, the verdict probe and the
160
+ * abuse intake down with the site. This is for the routes that serve the
161
+ * SITE ITSELF — a composed node tree — where continuing to serve is
162
+ * continuing to publish. `serve-media-cdn.ts` made the same call for bytes
163
+ * (AGL-1520); this is the node-tree half of it.
164
+ *
165
+ * Fails OPEN through `getSiteLockdown`, like every other reader here.
166
+ */ export async function visitorContentRefusal(options) {
167
+ var _options_nowMs;
168
+ const nowMs = (_options_nowMs = options.nowMs) != null ? _options_nowMs : Date.now();
169
+ const state = await getSiteLockdown(options.hostId, nowMs);
170
+ if (!lockdownBlocks(state, 'read')) return null;
171
+ return lockdownJsonResponse(state, options.surface ? {
172
+ notice: lockdownPausedNotice(options.surface)
173
+ } : undefined);
174
+ }
175
+
176
+ //# sourceMappingURL=tenant-write-lockdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/tenant-write-lockdown.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 * The lockdown gate for VISITOR writes on a customer's live site\n * (AGL-1511) — form submissions, cart changes, checkout.\n *\n * Why this is its own chokepoint rather than the console's. The tenant\n * middleware, which is where AGL-1501 enforces, **does not match `/api`**\n * (see the matcher in apps/tenant/middleware.ts): it exists to stop CACHED\n * PAGES serving. So the site's own write endpoints have had no lockdown\n * enforcement at all — under a full org takedown the pages 503 while a form\n * POST still writes. This closes that for both modes.\n *\n * The product decision this file encodes, from the issue: a visitor-facing\n * write on a read-serving site gets a **polite inline pause, never a\n * page-level 503**. A customer's site staying up and earning is the entire\n * reason read-only mode exists instead of full lockdown, and answering their\n * visitors with an outage page would spend exactly what the mode was\n * protecting. So the refusal is a 423 with visitor copy the block renders\n * inline, and the page around it never stops rendering.\n *\n * Under a FULL lock the same 423 is returned rather than a 503, and that is\n * deliberate too: the middleware has already replaced the whole page for\n * anyone loading it, so a write arriving here is a stale tab or a script,\n * and a machine-readable refusal is more useful to both than an HTML page.\n *\n * NO staff bypass and NO user scope, matching /api/lockdown-verdict: this is\n * a public website, the caller is anonymous, and a visitor's identity plays\n * no part in whether a site accepts an order.\n */\n\nimport {\n isLockdownActive,\n lockdownBlocks,\n type LockdownIntent,\n lockdownIntentForMethod,\n type LockdownPausedSurface,\n lockdownPausedNotice,\n type LockdownState,\n normalizeHostLockdown,\n normalizeOrgLockdown,\n resolveLockdown,\n} from '@aglyn/aglyn/server'\nimport {\n getPlatformLockdown,\n heldSiteTakedown,\n lockdownJsonResponse,\n rememberSiteTakedown,\n} from './lockdown'\nimport { getHostDocAdmin, getOrgForHost } from './organizations'\n\n/**\n * The active platform/org/host lockdown for one site, or null.\n *\n * Both reads are already paid by the callers on this path — the dispatcher\n * reads the host doc for its plugin deny-list and the org doc for\n * enablement, and both getters are `React.cache`-deduped per request — so\n * the gate costs one TTL-cached platform read and nothing else.\n *\n * FAIL OPEN on any error, matching every other lockdown reader: an\n * unreachable Firestore is an outage, not a lockdown, and a shop that stops\n * taking orders because a read timed out is a worse failure than the one\n * being guarded against.\n *\n * …EXCEPT for a takedown (AGL-1881, extending AGL-1621). One class of lock\n * inverts that cost function — \"the database was down\" is not an answer to a\n * court order — and until now the ledger that carries it covered only the\n * four scopes whose lock lives in `lockdowns/*`. Org and host locks live on\n * the org/host document, so the two scopes MOST likely to be carrying a\n * legal order were the two that fell through. Every successful read now\n * records its org and host verdict, and a failed read serves back whichever\n * of them was a takedown and is still active.\n *\n * Note what is NOT done here: the catch does not fail closed on the state it\n * could not read. On a read failure `suspendedEnforcement` is exactly as\n * unreadable as everything else, which is the whole reason a separate\n * durable ledger exists — and a catch that locked unconditionally would take\n * every shop on the platform offline on any transient blip, which is the\n * deliberate posture this keeps for the default case.\n */\nexport async function getSiteLockdown(\n hostId: string,\n nowMs = Date.now(),\n): Promise<LockdownState | null> {\n if (!hostId) return null\n try {\n const [platform, resolved, host] = await Promise.all([\n getPlatformLockdown(),\n getOrgForHost(hostId),\n getHostDocAdmin(hostId),\n ])\n const org = normalizeOrgLockdown(resolved?.org as never)\n const hostState = normalizeHostLockdown(host as never)\n rememberSiteTakedown(hostId, { org, host: hostState })\n const state = resolveLockdown({ platform, org, host: hostState }, nowMs)\n return isLockdownActive(state, nowMs) ? state : null\n } catch (error) {\n console.error('[tenant-write-lockdown] verdict failed', hostId, error)\n const held = heldSiteTakedown(hostId, nowMs)\n // The shipped fail-open answer, returned by the same statement it always\n // was, for every case where nothing takedown-class was ever observed.\n if (!held.org && !held.host) return null\n // The platform read is TTL-cached and swallows its own errors (it carries\n // its own ledger entry), so asking again here costs nothing and keeps a\n // wider lock from being masked by a narrower held one.\n const platform = await getPlatformLockdown().catch(() => null)\n const state = resolveLockdown(\n { platform, org: held.org, host: held.host },\n nowMs,\n )\n return isLockdownActive(state, nowMs) ? state : null\n }\n}\n\n/**\n * The refusal a visitor-facing write route returns, or null to proceed:\n *\n * const paused = await visitorWriteRefusal({ hostId, request, surface })\n * if (paused) return paused\n *\n * `surface` picks the WORDS only (`lockdownPausedNotice`) — the checkout\n * sentence has to promise no card was charged, and no generic copy can.\n */\nexport async function visitorWriteRefusal(options: {\n hostId: string\n /** The request whose method decides read vs write. */\n request?: { method?: string } | null\n /** Overrides `request`; `write` when neither is given. */\n intent?: LockdownIntent\n surface: LockdownPausedSurface\n nowMs?: number\n}): Promise<Response | null> {\n const nowMs = options.nowMs ?? Date.now()\n const intent =\n options.intent ??\n (options.request ? lockdownIntentForMethod(options.request.method) : 'write')\n // This is a WRITE gate: reads leave before any Firestore read, so the hot\n // unauthenticated GET path pays nothing. A full lock's refusal of tenant\n // API *reads* is not attempted here and is unchanged from today — the\n // middleware owns the page surface, and a read-refusing API gate is its\n // own decision with its own blast radius (filed separately).\n if (intent === 'read') return null\n const state = await getSiteLockdown(options.hostId, nowMs)\n if (!lockdownBlocks(state, intent)) return null\n return lockdownJsonResponse(state as LockdownState, {\n notice: lockdownPausedNotice(options.surface),\n })\n}\n\n/**\n * The refusal a visitor-facing route that SERVES SITE CONTENT over `/api`\n * returns under a FULL lock, or null to proceed (AGL-2495):\n *\n * const down = await visitorContentRefusal({ hostId })\n * if (down) return down\n *\n * The gap this closes, found by the AGL-1621 drill. The tenant middleware\n * is what takes a locked site off the air, and its matcher deliberately\n * excludes `/api` — so a full org or host takedown 503s every page while an\n * API route that composes and returns screen nodes keeps handing the site's\n * content out to anyone who can name a screen. `visitorWriteRefusal` above\n * cannot answer that: it leaves before any read, by design, because it is a\n * WRITE gate on the hot unauthenticated GET path.\n *\n * FULL locks only, and that is the whole point of it being a separate\n * helper rather than a flag on the write gate. Read-only mode exists so a\n * customer's site keeps serving and earning; refusing reads there would\n * spend exactly what the mode protects. `lockdownBlocks(state, 'read')` is\n * true only when the mode is full, which is precisely the case where the\n * pages are already gone.\n *\n * Deliberately NOT applied to the whole tenant API surface. A read-refusing\n * gate on every route would take the lock notice, the verdict probe and the\n * abuse intake down with the site. This is for the routes that serve the\n * SITE ITSELF — a composed node tree — where continuing to serve is\n * continuing to publish. `serve-media-cdn.ts` made the same call for bytes\n * (AGL-1520); this is the node-tree half of it.\n *\n * Fails OPEN through `getSiteLockdown`, like every other reader here.\n */\nexport async function visitorContentRefusal(options: {\n hostId: string\n surface?: LockdownPausedSurface\n nowMs?: number\n}): Promise<Response | null> {\n const nowMs = options.nowMs ?? Date.now()\n const state = await getSiteLockdown(options.hostId, nowMs)\n if (!lockdownBlocks(state, 'read')) return null\n return lockdownJsonResponse(\n state as LockdownState,\n options.surface ? { notice: lockdownPausedNotice(options.surface) } : undefined,\n )\n}\n"],"names":["isLockdownActive","lockdownBlocks","lockdownIntentForMethod","lockdownPausedNotice","normalizeHostLockdown","normalizeOrgLockdown","resolveLockdown","getPlatformLockdown","heldSiteTakedown","lockdownJsonResponse","rememberSiteTakedown","getHostDocAdmin","getOrgForHost","getSiteLockdown","hostId","nowMs","Date","now","platform","resolved","host","Promise","all","org","hostState","state","error","console","held","catch","visitorWriteRefusal","options","intent","request","method","notice","surface","visitorContentRefusal","undefined"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GAED,SACEA,gBAAgB,EAChBC,cAAc,EAEdC,uBAAuB,EAEvBC,oBAAoB,EAEpBC,qBAAqB,EACrBC,oBAAoB,EACpBC,eAAe,QACV,sBAAqB;AAC5B,SACEC,mBAAmB,EACnBC,gBAAgB,EAChBC,oBAAoB,EACpBC,oBAAoB,QACf,gBAAY;AACnB,SAASC,eAAe,EAAEC,aAAa,QAAQ,qBAAiB;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,eAAeC,gBACpBC,MAAc,EACdC,QAAQC,KAAKC,GAAG,EAAE;IAElB,IAAI,CAACH,QAAQ,OAAO;IACpB,IAAI;QACF,MAAM,CAACI,UAAUC,UAAUC,KAAK,GAAG,MAAMC,QAAQC,GAAG,CAAC;YACnDf;YACAK,cAAcE;YACdH,gBAAgBG;SACjB;QACD,MAAMS,MAAMlB,qBAAqBc,4BAAAA,SAAUI,GAAG;QAC9C,MAAMC,YAAYpB,sBAAsBgB;QACxCV,qBAAqBI,QAAQ;YAAES;YAAKH,MAAMI;QAAU;QACpD,MAAMC,QAAQnB,gBAAgB;YAAEY;YAAUK;YAAKH,MAAMI;QAAU,GAAGT;QAClE,OAAOf,iBAAiByB,OAAOV,SAASU,QAAQ;IAClD,EAAE,OAAOC,OAAO;QACdC,QAAQD,KAAK,CAAC,0CAA0CZ,QAAQY;QAChE,MAAME,OAAOpB,iBAAiBM,QAAQC;QACtC,yEAAyE;QACzE,sEAAsE;QACtE,IAAI,CAACa,KAAKL,GAAG,IAAI,CAACK,KAAKR,IAAI,EAAE,OAAO;QACpC,0EAA0E;QAC1E,wEAAwE;QACxE,uDAAuD;QACvD,MAAMF,WAAW,MAAMX,sBAAsBsB,KAAK,CAAC,IAAM;QACzD,MAAMJ,QAAQnB,gBACZ;YAAEY;YAAUK,KAAKK,KAAKL,GAAG;YAAEH,MAAMQ,KAAKR,IAAI;QAAC,GAC3CL;QAEF,OAAOf,iBAAiByB,OAAOV,SAASU,QAAQ;IAClD;AACF;AAEA;;;;;;;;CAQC,GACD,OAAO,eAAeK,oBAAoBC,OAQzC;QACeA,gBAEZA;IAFF,MAAMhB,SAAQgB,iBAAAA,QAAQhB,KAAK,YAAbgB,iBAAiBf,KAAKC,GAAG;IACvC,MAAMe,UACJD,kBAAAA,QAAQC,MAAM,YAAdD,kBACCA,QAAQE,OAAO,GAAG/B,wBAAwB6B,QAAQE,OAAO,CAACC,MAAM,IAAI;IACvE,0EAA0E;IAC1E,yEAAyE;IACzE,sEAAsE;IACtE,wEAAwE;IACxE,6DAA6D;IAC7D,IAAIF,WAAW,QAAQ,OAAO;IAC9B,MAAMP,QAAQ,MAAMZ,gBAAgBkB,QAAQjB,MAAM,EAAEC;IACpD,IAAI,CAACd,eAAewB,OAAOO,SAAS,OAAO;IAC3C,OAAOvB,qBAAqBgB,OAAwB;QAClDU,QAAQhC,qBAAqB4B,QAAQK,OAAO;IAC9C;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BC,GACD,OAAO,eAAeC,sBAAsBN,OAI3C;QACeA;IAAd,MAAMhB,SAAQgB,iBAAAA,QAAQhB,KAAK,YAAbgB,iBAAiBf,KAAKC,GAAG;IACvC,MAAMQ,QAAQ,MAAMZ,gBAAgBkB,QAAQjB,MAAM,EAAEC;IACpD,IAAI,CAACd,eAAewB,OAAO,SAAS,OAAO;IAC3C,OAAOhB,qBACLgB,OACAM,QAAQK,OAAO,GAAG;QAAED,QAAQhC,qBAAqB4B,QAAQK,OAAO;IAAE,IAAIE;AAE1E"}
@@ -0,0 +1,45 @@
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
+ export interface FakeFirestore {
18
+ /**
19
+ * Every document in a collection, keyed by id.
20
+ *
21
+ * A SUBcollection is addressed by its full path — `hosts/host-1/suppressions`
22
+ * — which is also how it is seeded.
23
+ */
24
+ docs: (collection: string) => Record<string, any>;
25
+ collection: (name: string) => any;
26
+ /**
27
+ * Several documents in one round trip, the shape the real `getAll` has:
28
+ * the results come back POSITIONALLY, one per reference, so a caller that
29
+ * zips them back against its own input list gets what it asked for.
30
+ */
31
+ getAll: (...refs: any[]) => Promise<any[]>;
32
+ /**
33
+ * A transaction, real enough for a read-then-write body.
34
+ *
35
+ * Reads pass straight through and writes are held until the body resolves,
36
+ * which is the property the code under test depends on: a writer that
37
+ * decides what to store FROM what it read must not observe its own
38
+ * half-finished write. It does not model contention or retries — nothing in
39
+ * this repo's transaction bodies branches on a retry, and a fake that
40
+ * pretended to would be asserting the fake.
41
+ */
42
+ runTransaction: <T>(body: (transaction: any) => Promise<T>) => Promise<T>;
43
+ }
44
+ export declare function fakeFirestore(seed?: Record<string, Record<string, any>>): FakeFirestore;
45
+ export default fakeFirestore;
@@ -0,0 +1,124 @@
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 Firestore stand-in for specs that span MORE THAN ONE COLLECTION
19
+ * (AGL-1592). Test-only; deliberately not exported from the barrel.
20
+ *
21
+ * The one-collection fakes elsewhere in this directory return the same
22
+ * document whatever collection name they are handed, which is fine when the
23
+ * code under test touches one document and actively misleading when it does
24
+ * not: the phone opt-out path reads `users/{uid}` and `contactSuppressions/{n}`
25
+ * and the whole point is that they are different records. A fake that conflated
26
+ * them would pass while the real thing wrote the marker on top of the
27
+ * suppression.
28
+ *
29
+ * `FieldValue.delete()` is honoured for real, because "the field is gone" and
30
+ * "the field holds a sentinel object" are different states and only the first
31
+ * one makes `seedUserProfile`'s blank check fire.
32
+ */ import { FieldValue } from "firebase-admin/firestore";
33
+ const DELETE = FieldValue.delete();
34
+ export function fakeFirestore(seed = {}) {
35
+ const store = JSON.parse(JSON.stringify(seed));
36
+ const snapshotFor = (collection, id)=>{
37
+ var _store_collection;
38
+ const data = (_store_collection = store[collection]) == null ? void 0 : _store_collection[id];
39
+ return {
40
+ id,
41
+ exists: data !== undefined,
42
+ get: (field)=>data == null ? void 0 : data[field],
43
+ data: ()=>data
44
+ };
45
+ };
46
+ const fake = {
47
+ docs: (collection)=>{
48
+ var _store_collection;
49
+ return (_store_collection = store[collection]) != null ? _store_collection : {};
50
+ },
51
+ getAll: async (...refs)=>refs.map((ref)=>snapshotFor(ref.collectionPath, ref.id)),
52
+ async runTransaction (body) {
53
+ const pending = [];
54
+ const transaction = {
55
+ get: (ref)=>ref.get(),
56
+ set: (ref, data, options)=>{
57
+ pending.push(()=>ref.set(data, options));
58
+ return transaction;
59
+ }
60
+ };
61
+ const result = await body(transaction);
62
+ for (const write of pending)await write();
63
+ return result;
64
+ },
65
+ collection (name) {
66
+ var _store_name;
67
+ store[name] = (_store_name = store[name]) != null ? _store_name : {};
68
+ const api = {
69
+ doc: (id)=>({
70
+ id,
71
+ /**
72
+ * Which collection this reference came from, so `getAll` can resolve
73
+ * a bare reference the way the real client does.
74
+ */ collectionPath: name,
75
+ /**
76
+ * A subcollection is a collection at the joined path, so the fake
77
+ * stays one flat map. Nesting matters here because a per-site list
78
+ * lives under the site — a fake that returned the same documents for
79
+ * `hosts/a/suppressions` and `hosts/b/suppressions` would certify a
80
+ * sender that leaks one site's unsubscribes into another's.
81
+ */ collection: (sub)=>fake.collection(`${name}/${id}/${sub}`),
82
+ get: async ()=>snapshotFor(name, id),
83
+ set: async (data, options)=>{
84
+ var _store_name_id;
85
+ const base = (options == null ? void 0 : options.merge) ? (_store_name_id = store[name][id]) != null ? _store_name_id : {} : {};
86
+ const next = _extends({}, base);
87
+ for (const [key, value] of Object.entries(data)){
88
+ // A real delete, not a stored sentinel — see the header.
89
+ if (value && typeof value === 'object' && DELETE.isEqual(value)) {
90
+ delete next[key];
91
+ continue;
92
+ }
93
+ // Server timestamps arrive as sentinels too. Freeze them into
94
+ // something a spec can assert on and a date can be read from.
95
+ next[key] = value && typeof value === 'object' && typeof value.isEqual === 'function' ? {
96
+ seconds: Math.floor(Date.now() / 1000)
97
+ } : value;
98
+ }
99
+ store[name][id] = next;
100
+ }
101
+ }),
102
+ // Ordering and limits are not what these specs are about; the queue
103
+ // read just needs to return everything it stored.
104
+ orderBy: ()=>api,
105
+ limit: ()=>api,
106
+ get: async ()=>({
107
+ docs: Object.keys(store[name]).map((id)=>snapshotFor(name, id))
108
+ }),
109
+ add: async (data)=>{
110
+ const id = `auto-${Object.keys(store[name]).length + 1}`;
111
+ store[name][id] = data;
112
+ return {
113
+ id
114
+ };
115
+ }
116
+ };
117
+ return api;
118
+ }
119
+ };
120
+ return fake;
121
+ }
122
+ export default fakeFirestore;
123
+
124
+ //# sourceMappingURL=test-firestore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/tenant/data/admin/src/lib/server/test-firestore.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 Firestore stand-in for specs that span MORE THAN ONE COLLECTION\n * (AGL-1592). Test-only; deliberately not exported from the barrel.\n *\n * The one-collection fakes elsewhere in this directory return the same\n * document whatever collection name they are handed, which is fine when the\n * code under test touches one document and actively misleading when it does\n * not: the phone opt-out path reads `users/{uid}` and `contactSuppressions/{n}`\n * and the whole point is that they are different records. A fake that conflated\n * them would pass while the real thing wrote the marker on top of the\n * suppression.\n *\n * `FieldValue.delete()` is honoured for real, because \"the field is gone\" and\n * \"the field holds a sentinel object\" are different states and only the first\n * one makes `seedUserProfile`'s blank check fire.\n */\n\nimport { FieldValue } from 'firebase-admin/firestore'\n\nconst DELETE = FieldValue.delete()\n\nexport interface FakeFirestore {\n /**\n * Every document in a collection, keyed by id.\n *\n * A SUBcollection is addressed by its full path — `hosts/host-1/suppressions`\n * — which is also how it is seeded.\n */\n docs: (collection: string) => Record<string, any>\n collection: (name: string) => any\n /**\n * Several documents in one round trip, the shape the real `getAll` has:\n * the results come back POSITIONALLY, one per reference, so a caller that\n * zips them back against its own input list gets what it asked for.\n */\n getAll: (...refs: any[]) => Promise<any[]>\n /**\n * A transaction, real enough for a read-then-write body.\n *\n * Reads pass straight through and writes are held until the body resolves,\n * which is the property the code under test depends on: a writer that\n * decides what to store FROM what it read must not observe its own\n * half-finished write. It does not model contention or retries — nothing in\n * this repo's transaction bodies branches on a retry, and a fake that\n * pretended to would be asserting the fake.\n */\n runTransaction: <T>(body: (transaction: any) => Promise<T>) => Promise<T>\n}\n\nexport function fakeFirestore(\n seed: Record<string, Record<string, any>> = {},\n): FakeFirestore {\n const store: Record<string, Record<string, any>> = JSON.parse(\n JSON.stringify(seed),\n )\n\n const snapshotFor = (collection: string, id: string) => {\n const data = store[collection]?.[id]\n return {\n id,\n exists: data !== undefined,\n get: (field: string) => data?.[field],\n data: () => data,\n }\n }\n\n const fake: FakeFirestore = {\n docs: (collection: string) => store[collection] ?? {},\n getAll: async (...refs: any[]) =>\n refs.map((ref) => snapshotFor(ref.collectionPath, ref.id)),\n async runTransaction<T>(body: (transaction: any) => Promise<T>): Promise<T> {\n const pending: Array<() => Promise<void>> = []\n const transaction = {\n get: (ref: any) => ref.get(),\n set: (ref: any, data: Record<string, any>, options?: { merge?: boolean }) => {\n pending.push(() => ref.set(data, options))\n return transaction\n },\n }\n const result = await body(transaction)\n for (const write of pending) await write()\n return result\n },\n collection(name: string) {\n store[name] = store[name] ?? {}\n const api: any = {\n doc: (id: string) => ({\n id,\n /**\n * Which collection this reference came from, so `getAll` can resolve\n * a bare reference the way the real client does.\n */\n collectionPath: name,\n /**\n * A subcollection is a collection at the joined path, so the fake\n * stays one flat map. Nesting matters here because a per-site list\n * lives under the site — a fake that returned the same documents for\n * `hosts/a/suppressions` and `hosts/b/suppressions` would certify a\n * sender that leaks one site's unsubscribes into another's.\n */\n collection: (sub: string) => fake.collection(`${name}/${id}/${sub}`),\n get: async () => snapshotFor(name, id),\n set: async (data: Record<string, any>, options?: { merge?: boolean }) => {\n const base = options?.merge ? (store[name][id] ?? {}) : {}\n const next: Record<string, any> = { ...base }\n for (const [key, value] of Object.entries(data)) {\n // A real delete, not a stored sentinel — see the header.\n if (value && typeof value === 'object' && DELETE.isEqual(value as any)) {\n delete next[key]\n continue\n }\n // Server timestamps arrive as sentinels too. Freeze them into\n // something a spec can assert on and a date can be read from.\n next[key] =\n value && typeof value === 'object' && typeof (value as any).isEqual === 'function'\n ? { seconds: Math.floor(Date.now() / 1000) }\n : value\n }\n store[name][id] = next\n },\n }),\n // Ordering and limits are not what these specs are about; the queue\n // read just needs to return everything it stored.\n orderBy: () => api,\n limit: () => api,\n get: async () => ({\n docs: Object.keys(store[name]).map((id) => snapshotFor(name, id)),\n }),\n add: async (data: Record<string, any>) => {\n const id = `auto-${Object.keys(store[name]).length + 1}`\n store[name][id] = data\n return { id }\n },\n }\n return api\n },\n }\n return fake\n}\n\nexport default fakeFirestore\n"],"names":["FieldValue","DELETE","delete","fakeFirestore","seed","store","JSON","parse","stringify","snapshotFor","collection","id","data","exists","undefined","get","field","fake","docs","getAll","refs","map","ref","collectionPath","runTransaction","body","pending","transaction","set","options","push","result","write","name","api","doc","sub","base","merge","next","key","value","Object","entries","isEqual","seconds","Math","floor","Date","now","orderBy","limit","keys","add","length"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;CAeC,GAED,SAASA,UAAU,QAAQ,2BAA0B;AAErD,MAAMC,SAASD,WAAWE,MAAM;AA8BhC,OAAO,SAASC,cACdC,OAA4C,CAAC,CAAC;IAE9C,MAAMC,QAA6CC,KAAKC,KAAK,CAC3DD,KAAKE,SAAS,CAACJ;IAGjB,MAAMK,cAAc,CAACC,YAAoBC;YAC1BN;QAAb,MAAMO,QAAOP,oBAAAA,KAAK,CAACK,WAAW,qBAAjBL,iBAAmB,CAACM,GAAG;QACpC,OAAO;YACLA;YACAE,QAAQD,SAASE;YACjBC,KAAK,CAACC,QAAkBJ,wBAAAA,IAAM,CAACI,MAAM;YACrCJ,MAAM,IAAMA;QACd;IACF;IAEA,MAAMK,OAAsB;QAC1BC,MAAM,CAACR;gBAAuBL;oBAAAA,oBAAAA,KAAK,CAACK,WAAW,YAAjBL,oBAAqB,CAAC;;QACpDc,QAAQ,OAAO,GAAGC,OAChBA,KAAKC,GAAG,CAAC,CAACC,MAAQb,YAAYa,IAAIC,cAAc,EAAED,IAAIX,EAAE;QAC1D,MAAMa,gBAAkBC,IAAsC;YAC5D,MAAMC,UAAsC,EAAE;YAC9C,MAAMC,cAAc;gBAClBZ,KAAK,CAACO,MAAaA,IAAIP,GAAG;gBAC1Ba,KAAK,CAACN,KAAUV,MAA2BiB;oBACzCH,QAAQI,IAAI,CAAC,IAAMR,IAAIM,GAAG,CAAChB,MAAMiB;oBACjC,OAAOF;gBACT;YACF;YACA,MAAMI,SAAS,MAAMN,KAAKE;YAC1B,KAAK,MAAMK,SAASN,QAAS,MAAMM;YACnC,OAAOD;QACT;QACArB,YAAWuB,IAAY;gBACP5B;YAAdA,KAAK,CAAC4B,KAAK,IAAG5B,cAAAA,KAAK,CAAC4B,KAAK,YAAX5B,cAAe,CAAC;YAC9B,MAAM6B,MAAW;gBACfC,KAAK,CAACxB,KAAgB,CAAA;wBACpBA;wBACA;;;WAGC,GACDY,gBAAgBU;wBAChB;;;;;;WAMC,GACDvB,YAAY,CAAC0B,MAAgBnB,KAAKP,UAAU,CAAC,GAAGuB,KAAK,CAAC,EAAEtB,GAAG,CAAC,EAAEyB,KAAK;wBACnErB,KAAK,UAAYN,YAAYwB,MAAMtB;wBACnCiB,KAAK,OAAOhB,MAA2BiB;gCACNxB;4BAA/B,MAAMgC,OAAOR,CAAAA,2BAAAA,QAASS,KAAK,KAAIjC,iBAAAA,KAAK,CAAC4B,KAAK,CAACtB,GAAG,YAAfN,iBAAmB,CAAC,IAAK,CAAC;4BACzD,MAAMkC,OAA4B,aAAKF;4BACvC,KAAK,MAAM,CAACG,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAAC/B,MAAO;gCAC/C,yDAAyD;gCACzD,IAAI6B,SAAS,OAAOA,UAAU,YAAYxC,OAAO2C,OAAO,CAACH,QAAe;oCACtE,OAAOF,IAAI,CAACC,IAAI;oCAChB;gCACF;gCACA,8DAA8D;gCAC9D,8DAA8D;gCAC9DD,IAAI,CAACC,IAAI,GACPC,SAAS,OAAOA,UAAU,YAAY,OAAO,AAACA,MAAcG,OAAO,KAAK,aACpE;oCAAEC,SAASC,KAAKC,KAAK,CAACC,KAAKC,GAAG,KAAK;gCAAM,IACzCR;4BACR;4BACApC,KAAK,CAAC4B,KAAK,CAACtB,GAAG,GAAG4B;wBACpB;oBACF,CAAA;gBACA,oEAAoE;gBACpE,kDAAkD;gBAClDW,SAAS,IAAMhB;gBACfiB,OAAO,IAAMjB;gBACbnB,KAAK,UAAa,CAAA;wBAChBG,MAAMwB,OAAOU,IAAI,CAAC/C,KAAK,CAAC4B,KAAK,EAAEZ,GAAG,CAAC,CAACV,KAAOF,YAAYwB,MAAMtB;oBAC/D,CAAA;gBACA0C,KAAK,OAAOzC;oBACV,MAAMD,KAAK,CAAC,KAAK,EAAE+B,OAAOU,IAAI,CAAC/C,KAAK,CAAC4B,KAAK,EAAEqB,MAAM,GAAG,GAAG;oBACxDjD,KAAK,CAAC4B,KAAK,CAACtB,GAAG,GAAGC;oBAClB,OAAO;wBAAED;oBAAG;gBACd;YACF;YACA,OAAOuB;QACT;IACF;IACA,OAAOjB;AACT;AAEA,eAAed,cAAa"}