@aglyn/aglyn 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 (963) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +11 -0
  3. package/package.json +66 -0
  4. package/src/index.d.ts +25 -0
  5. package/src/index.js +26 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/aglyn.d.ts +31 -0
  8. package/src/lib/aglyn.js +114 -0
  9. package/src/lib/aglyn.js.map +1 -0
  10. package/src/lib/app-utils/abuse-report.d.ts +193 -0
  11. package/src/lib/app-utils/abuse-report.js +321 -0
  12. package/src/lib/app-utils/abuse-report.js.map +1 -0
  13. package/src/lib/app-utils/accept-negotiation.d.ts +129 -0
  14. package/src/lib/app-utils/accept-negotiation.js +266 -0
  15. package/src/lib/app-utils/accept-negotiation.js.map +1 -0
  16. package/src/lib/app-utils/account-emails.d.ts +263 -0
  17. package/src/lib/app-utils/account-emails.js +264 -0
  18. package/src/lib/app-utils/account-emails.js.map +1 -0
  19. package/src/lib/app-utils/actions.d.ts +717 -0
  20. package/src/lib/app-utils/actions.js +1031 -0
  21. package/src/lib/app-utils/actions.js.map +1 -0
  22. package/src/lib/app-utils/activity-presenter.d.ts +152 -0
  23. package/src/lib/app-utils/activity-presenter.js +326 -0
  24. package/src/lib/app-utils/activity-presenter.js.map +1 -0
  25. package/src/lib/app-utils/advertising-tag-mounts.d.ts +133 -0
  26. package/src/lib/app-utils/advertising-tag-mounts.js +118 -0
  27. package/src/lib/app-utils/advertising-tag-mounts.js.map +1 -0
  28. package/src/lib/app-utils/advertising-tags.d.ts +307 -0
  29. package/src/lib/app-utils/advertising-tags.js +525 -0
  30. package/src/lib/app-utils/advertising-tags.js.map +1 -0
  31. package/src/lib/app-utils/agent-api-catalog.d.ts +94 -0
  32. package/src/lib/app-utils/agent-api-catalog.js +106 -0
  33. package/src/lib/app-utils/agent-api-catalog.js.map +1 -0
  34. package/src/lib/app-utils/agent-openapi.d.ts +103 -0
  35. package/src/lib/app-utils/agent-openapi.js +1128 -0
  36. package/src/lib/app-utils/agent-openapi.js.map +1 -0
  37. package/src/lib/app-utils/analytics-beacon.d.ts +154 -0
  38. package/src/lib/app-utils/analytics-beacon.js +152 -0
  39. package/src/lib/app-utils/analytics-beacon.js.map +1 -0
  40. package/src/lib/app-utils/analytics-environment.d.ts +133 -0
  41. package/src/lib/app-utils/analytics-environment.js +152 -0
  42. package/src/lib/app-utils/analytics-environment.js.map +1 -0
  43. package/src/lib/app-utils/analytics-events.d.ts +744 -0
  44. package/src/lib/app-utils/analytics-events.js +650 -0
  45. package/src/lib/app-utils/analytics-events.js.map +1 -0
  46. package/src/lib/app-utils/analytics-link-clicks.d.ts +102 -0
  47. package/src/lib/app-utils/analytics-link-clicks.js +267 -0
  48. package/src/lib/app-utils/analytics-link-clicks.js.map +1 -0
  49. package/src/lib/app-utils/analytics-path-key.d.ts +46 -0
  50. package/src/lib/app-utils/analytics-path-key.js +46 -0
  51. package/src/lib/app-utils/analytics-path-key.js.map +1 -0
  52. package/src/lib/app-utils/api-adapter.d.ts +47 -0
  53. package/src/lib/app-utils/api-adapter.js +367 -0
  54. package/src/lib/app-utils/api-adapter.js.map +1 -0
  55. package/src/lib/app-utils/api-idempotency.d.ts +169 -0
  56. package/src/lib/app-utils/api-idempotency.js +213 -0
  57. package/src/lib/app-utils/api-idempotency.js.map +1 -0
  58. package/src/lib/app-utils/api-plugins.d.ts +210 -0
  59. package/src/lib/app-utils/api-plugins.js +221 -0
  60. package/src/lib/app-utils/api-plugins.js.map +1 -0
  61. package/src/lib/app-utils/assist-credits.d.ts +482 -0
  62. package/src/lib/app-utils/assist-credits.js +509 -0
  63. package/src/lib/app-utils/assist-credits.js.map +1 -0
  64. package/src/lib/app-utils/attribution-attribute.d.ts +34 -0
  65. package/src/lib/app-utils/attribution-attribute.js +34 -0
  66. package/src/lib/app-utils/attribution-attribute.js.map +1 -0
  67. package/src/lib/app-utils/attribution-guard.d.ts +130 -0
  68. package/src/lib/app-utils/attribution-guard.js +410 -0
  69. package/src/lib/app-utils/attribution-guard.js.map +1 -0
  70. package/src/lib/app-utils/author-css.d.ts +123 -0
  71. package/src/lib/app-utils/author-css.js +275 -0
  72. package/src/lib/app-utils/author-css.js.map +1 -0
  73. package/src/lib/app-utils/author-html-markdown.d.ts +31 -0
  74. package/src/lib/app-utils/author-html-markdown.js +497 -0
  75. package/src/lib/app-utils/author-html-markdown.js.map +1 -0
  76. package/src/lib/app-utils/author-html.d.ts +135 -0
  77. package/src/lib/app-utils/author-html.js +1037 -0
  78. package/src/lib/app-utils/author-html.js.map +1 -0
  79. package/src/lib/app-utils/automation-placeholders.d.ts +72 -0
  80. package/src/lib/app-utils/automation-placeholders.js +207 -0
  81. package/src/lib/app-utils/automation-placeholders.js.map +1 -0
  82. package/src/lib/app-utils/bandwidth-cap.d.ts +183 -0
  83. package/src/lib/app-utils/bandwidth-cap.js +193 -0
  84. package/src/lib/app-utils/bandwidth-cap.js.map +1 -0
  85. package/src/lib/app-utils/binding-token-catalog.d.ts +82 -0
  86. package/src/lib/app-utils/binding-token-catalog.js +293 -0
  87. package/src/lib/app-utils/binding-token-catalog.js.map +1 -0
  88. package/src/lib/app-utils/binding-tokens.d.ts +157 -0
  89. package/src/lib/app-utils/binding-tokens.js +251 -0
  90. package/src/lib/app-utils/binding-tokens.js.map +1 -0
  91. package/src/lib/app-utils/breadcrumb-json-ld.d.ts +57 -0
  92. package/src/lib/app-utils/breadcrumb-json-ld.js +67 -0
  93. package/src/lib/app-utils/breadcrumb-json-ld.js.map +1 -0
  94. package/src/lib/app-utils/campaign-attribution.d.ts +117 -0
  95. package/src/lib/app-utils/campaign-attribution.js +162 -0
  96. package/src/lib/app-utils/campaign-attribution.js.map +1 -0
  97. package/src/lib/app-utils/campaign-forwarding.d.ts +220 -0
  98. package/src/lib/app-utils/campaign-forwarding.js +332 -0
  99. package/src/lib/app-utils/campaign-forwarding.js.map +1 -0
  100. package/src/lib/app-utils/campaign-membership.d.ts +109 -0
  101. package/src/lib/app-utils/campaign-membership.js +176 -0
  102. package/src/lib/app-utils/campaign-membership.js.map +1 -0
  103. package/src/lib/app-utils/campaign-touch.d.ts +206 -0
  104. package/src/lib/app-utils/campaign-touch.js +326 -0
  105. package/src/lib/app-utils/campaign-touch.js.map +1 -0
  106. package/src/lib/app-utils/child-contract-compose.d.ts +28 -0
  107. package/src/lib/app-utils/child-contract-compose.js +140 -0
  108. package/src/lib/app-utils/child-contract-compose.js.map +1 -0
  109. package/src/lib/app-utils/child-contract.d.ts +129 -0
  110. package/src/lib/app-utils/child-contract.js +153 -0
  111. package/src/lib/app-utils/child-contract.js.map +1 -0
  112. package/src/lib/app-utils/collection-delete.d.ts +113 -0
  113. package/src/lib/app-utils/collection-delete.js +120 -0
  114. package/src/lib/app-utils/collection-delete.js.map +1 -0
  115. package/src/lib/app-utils/collection-entries.d.ts +833 -0
  116. package/src/lib/app-utils/collection-entries.js +1285 -0
  117. package/src/lib/app-utils/collection-entries.js.map +1 -0
  118. package/src/lib/app-utils/collection-entry-date.d.ts +129 -0
  119. package/src/lib/app-utils/collection-entry-date.js +183 -0
  120. package/src/lib/app-utils/collection-entry-date.js.map +1 -0
  121. package/src/lib/app-utils/collection-kind.d.ts +55 -0
  122. package/src/lib/app-utils/collection-kind.js +69 -0
  123. package/src/lib/app-utils/collection-kind.js.map +1 -0
  124. package/src/lib/app-utils/collection-slug.d.ts +79 -0
  125. package/src/lib/app-utils/collection-slug.js +70 -0
  126. package/src/lib/app-utils/collection-slug.js.map +1 -0
  127. package/src/lib/app-utils/compose-layout-nodes.d.ts +147 -0
  128. package/src/lib/app-utils/compose-layout-nodes.js +261 -0
  129. package/src/lib/app-utils/compose-layout-nodes.js.map +1 -0
  130. package/src/lib/app-utils/compose-layout-props.d.ts +71 -0
  131. package/src/lib/app-utils/compose-layout-props.js +61 -0
  132. package/src/lib/app-utils/compose-layout-props.js.map +1 -0
  133. package/src/lib/app-utils/compose-reusable-components.d.ts +504 -0
  134. package/src/lib/app-utils/compose-reusable-components.js +1366 -0
  135. package/src/lib/app-utils/compose-reusable-components.js.map +1 -0
  136. package/src/lib/app-utils/compress.d.ts +35 -0
  137. package/src/lib/app-utils/compress.js +38 -0
  138. package/src/lib/app-utils/compress.js.map +1 -0
  139. package/src/lib/app-utils/consent-banner-ui.d.ts +226 -0
  140. package/src/lib/app-utils/consent-banner-ui.js +583 -0
  141. package/src/lib/app-utils/consent-banner-ui.js.map +1 -0
  142. package/src/lib/app-utils/consent-groups.d.ts +189 -0
  143. package/src/lib/app-utils/consent-groups.js +219 -0
  144. package/src/lib/app-utils/consent-groups.js.map +1 -0
  145. package/src/lib/app-utils/consent-preferences-dialog.d.ts +58 -0
  146. package/src/lib/app-utils/consent-preferences-dialog.js +106 -0
  147. package/src/lib/app-utils/consent-preferences-dialog.js.map +1 -0
  148. package/src/lib/app-utils/console-record-links.d.ts +163 -0
  149. package/src/lib/app-utils/console-record-links.js +178 -0
  150. package/src/lib/app-utils/console-record-links.js.map +1 -0
  151. package/src/lib/app-utils/console-routes.d.ts +658 -0
  152. package/src/lib/app-utils/console-routes.js +489 -0
  153. package/src/lib/app-utils/console-routes.js.map +1 -0
  154. package/src/lib/app-utils/console-widget-slot-context.d.ts +46 -0
  155. package/src/lib/app-utils/console-widget-slot-context.js +23 -0
  156. package/src/lib/app-utils/console-widget-slot-context.js.map +1 -0
  157. package/src/lib/app-utils/contact-custom-fields.d.ts +191 -0
  158. package/src/lib/app-utils/contact-custom-fields.js +349 -0
  159. package/src/lib/app-utils/contact-custom-fields.js.map +1 -0
  160. package/src/lib/app-utils/contact-holder.d.ts +52 -0
  161. package/src/lib/app-utils/contact-holder.js +98 -0
  162. package/src/lib/app-utils/contact-holder.js.map +1 -0
  163. package/src/lib/app-utils/contact-merge.d.ts +136 -0
  164. package/src/lib/app-utils/contact-merge.js +379 -0
  165. package/src/lib/app-utils/contact-merge.js.map +1 -0
  166. package/src/lib/app-utils/contacts.d.ts +554 -0
  167. package/src/lib/app-utils/contacts.js +401 -0
  168. package/src/lib/app-utils/contacts.js.map +1 -0
  169. package/src/lib/app-utils/content-author-profile.d.ts +68 -0
  170. package/src/lib/app-utils/content-author-profile.js +118 -0
  171. package/src/lib/app-utils/content-author-profile.js.map +1 -0
  172. package/src/lib/app-utils/content-authors.d.ts +662 -0
  173. package/src/lib/app-utils/content-authors.js +771 -0
  174. package/src/lib/app-utils/content-authors.js.map +1 -0
  175. package/src/lib/app-utils/content-schema-type.d.ts +81 -0
  176. package/src/lib/app-utils/content-schema-type.js +87 -0
  177. package/src/lib/app-utils/content-schema-type.js.map +1 -0
  178. package/src/lib/app-utils/contexts.d.ts +35 -0
  179. package/src/lib/app-utils/contexts.js +35 -0
  180. package/src/lib/app-utils/contexts.js.map +1 -0
  181. package/src/lib/app-utils/create-resource-uid.d.ts +18 -0
  182. package/src/lib/app-utils/create-resource-uid.js +23 -0
  183. package/src/lib/app-utils/create-resource-uid.js.map +1 -0
  184. package/src/lib/app-utils/crm-booking.d.ts +75 -0
  185. package/src/lib/app-utils/crm-booking.js +133 -0
  186. package/src/lib/app-utils/crm-booking.js.map +1 -0
  187. package/src/lib/app-utils/crm-csv.d.ts +232 -0
  188. package/src/lib/app-utils/crm-csv.js +432 -0
  189. package/src/lib/app-utils/crm-csv.js.map +1 -0
  190. package/src/lib/app-utils/crm-digest.d.ts +179 -0
  191. package/src/lib/app-utils/crm-digest.js +271 -0
  192. package/src/lib/app-utils/crm-digest.js.map +1 -0
  193. package/src/lib/app-utils/crm-email-templates.d.ts +172 -0
  194. package/src/lib/app-utils/crm-email-templates.js +382 -0
  195. package/src/lib/app-utils/crm-email-templates.js.map +1 -0
  196. package/src/lib/app-utils/crm-inbound.d.ts +245 -0
  197. package/src/lib/app-utils/crm-inbound.js +407 -0
  198. package/src/lib/app-utils/crm-inbound.js.map +1 -0
  199. package/src/lib/app-utils/crm-kinds.d.ts +73 -0
  200. package/src/lib/app-utils/crm-kinds.js +92 -0
  201. package/src/lib/app-utils/crm-kinds.js.map +1 -0
  202. package/src/lib/app-utils/crm-next-activity.d.ts +107 -0
  203. package/src/lib/app-utils/crm-next-activity.js +140 -0
  204. package/src/lib/app-utils/crm-next-activity.js.map +1 -0
  205. package/src/lib/app-utils/crm-reports.d.ts +479 -0
  206. package/src/lib/app-utils/crm-reports.js +680 -0
  207. package/src/lib/app-utils/crm-reports.js.map +1 -0
  208. package/src/lib/app-utils/crm-task-reminders.d.ts +129 -0
  209. package/src/lib/app-utils/crm-task-reminders.js +139 -0
  210. package/src/lib/app-utils/crm-task-reminders.js.map +1 -0
  211. package/src/lib/app-utils/crm.d.ts +1546 -0
  212. package/src/lib/app-utils/crm.js +1814 -0
  213. package/src/lib/app-utils/crm.js.map +1 -0
  214. package/src/lib/app-utils/csp-report.d.ts +84 -0
  215. package/src/lib/app-utils/csp-report.js +177 -0
  216. package/src/lib/app-utils/csp-report.js.map +1 -0
  217. package/src/lib/app-utils/csv-import.d.ts +192 -0
  218. package/src/lib/app-utils/csv-import.js +279 -0
  219. package/src/lib/app-utils/csv-import.js.map +1 -0
  220. package/src/lib/app-utils/dataset-csv.d.ts +109 -0
  221. package/src/lib/app-utils/dataset-csv.js +235 -0
  222. package/src/lib/app-utils/dataset-csv.js.map +1 -0
  223. package/src/lib/app-utils/dataset-models.d.ts +194 -0
  224. package/src/lib/app-utils/dataset-models.js +409 -0
  225. package/src/lib/app-utils/dataset-models.js.map +1 -0
  226. package/src/lib/app-utils/dataset-query.d.ts +57 -0
  227. package/src/lib/app-utils/dataset-query.js +126 -0
  228. package/src/lib/app-utils/dataset-query.js.map +1 -0
  229. package/src/lib/app-utils/datasets.d.ts +136 -0
  230. package/src/lib/app-utils/datasets.js +177 -0
  231. package/src/lib/app-utils/datasets.js.map +1 -0
  232. package/src/lib/app-utils/decompress.d.ts +36 -0
  233. package/src/lib/app-utils/decompress.js +32 -0
  234. package/src/lib/app-utils/decompress.js.map +1 -0
  235. package/src/lib/app-utils/dedicated-sending-domain.d.ts +47 -0
  236. package/src/lib/app-utils/dedicated-sending-domain.js +114 -0
  237. package/src/lib/app-utils/dedicated-sending-domain.js.map +1 -0
  238. package/src/lib/app-utils/deferred-image.d.ts +86 -0
  239. package/src/lib/app-utils/deferred-image.js +83 -0
  240. package/src/lib/app-utils/deferred-image.js.map +1 -0
  241. package/src/lib/app-utils/definition-canvas-tree.d.ts +61 -0
  242. package/src/lib/app-utils/definition-canvas-tree.js +126 -0
  243. package/src/lib/app-utils/definition-canvas-tree.js.map +1 -0
  244. package/src/lib/app-utils/deployment-shape.d.ts +167 -0
  245. package/src/lib/app-utils/deployment-shape.js +238 -0
  246. package/src/lib/app-utils/deployment-shape.js.map +1 -0
  247. package/src/lib/app-utils/detect-template-placeholders.d.ts +30 -0
  248. package/src/lib/app-utils/detect-template-placeholders.js +53 -0
  249. package/src/lib/app-utils/detect-template-placeholders.js.map +1 -0
  250. package/src/lib/app-utils/dmca-counter-notice.d.ts +185 -0
  251. package/src/lib/app-utils/dmca-counter-notice.js +277 -0
  252. package/src/lib/app-utils/dmca-counter-notice.js.map +1 -0
  253. package/src/lib/app-utils/docs-deflection.d.ts +156 -0
  254. package/src/lib/app-utils/docs-deflection.js +547 -0
  255. package/src/lib/app-utils/docs-deflection.js.map +1 -0
  256. package/src/lib/app-utils/docs-help.d.ts +57 -0
  257. package/src/lib/app-utils/docs-help.generated.d.ts +303 -0
  258. package/src/lib/app-utils/docs-help.generated.js +867 -0
  259. package/src/lib/app-utils/docs-help.generated.js.map +1 -0
  260. package/src/lib/app-utils/docs-help.js +46 -0
  261. package/src/lib/app-utils/docs-help.js.map +1 -0
  262. package/src/lib/app-utils/docs-index.generated.d.ts +29 -0
  263. package/src/lib/app-utils/docs-index.generated.js +8388 -0
  264. package/src/lib/app-utils/docs-index.generated.js.map +1 -0
  265. package/src/lib/app-utils/docs-retrieval.d.ts +67 -0
  266. package/src/lib/app-utils/docs-retrieval.js +188 -0
  267. package/src/lib/app-utils/docs-retrieval.js.map +1 -0
  268. package/src/lib/app-utils/document-landmark.d.ts +50 -0
  269. package/src/lib/app-utils/document-landmark.js +100 -0
  270. package/src/lib/app-utils/document-landmark.js.map +1 -0
  271. package/src/lib/app-utils/duplicate-resource.d.ts +101 -0
  272. package/src/lib/app-utils/duplicate-resource.js +206 -0
  273. package/src/lib/app-utils/duplicate-resource.js.map +1 -0
  274. package/src/lib/app-utils/dynamic-list-rule.d.ts +471 -0
  275. package/src/lib/app-utils/dynamic-list-rule.js +914 -0
  276. package/src/lib/app-utils/dynamic-list-rule.js.map +1 -0
  277. package/src/lib/app-utils/element-animation.d.ts +200 -0
  278. package/src/lib/app-utils/element-animation.js +233 -0
  279. package/src/lib/app-utils/element-animation.js.map +1 -0
  280. package/src/lib/app-utils/element-hidden-style.d.ts +46 -0
  281. package/src/lib/app-utils/element-hidden-style.js +48 -0
  282. package/src/lib/app-utils/element-hidden-style.js.map +1 -0
  283. package/src/lib/app-utils/element-ui.d.ts +243 -0
  284. package/src/lib/app-utils/element-ui.js +459 -0
  285. package/src/lib/app-utils/element-ui.js.map +1 -0
  286. package/src/lib/app-utils/email-starter-policy.d.ts +160 -0
  287. package/src/lib/app-utils/email-starter-policy.js +327 -0
  288. package/src/lib/app-utils/email-starter-policy.js.map +1 -0
  289. package/src/lib/app-utils/email-topics.d.ts +310 -0
  290. package/src/lib/app-utils/email-topics.js +336 -0
  291. package/src/lib/app-utils/email-topics.js.map +1 -0
  292. package/src/lib/app-utils/enabled-plugins-context.d.ts +100 -0
  293. package/src/lib/app-utils/enabled-plugins-context.js +113 -0
  294. package/src/lib/app-utils/enabled-plugins-context.js.map +1 -0
  295. package/src/lib/app-utils/ensure-canvas-root.d.ts +38 -0
  296. package/src/lib/app-utils/ensure-canvas-root.js +61 -0
  297. package/src/lib/app-utils/ensure-canvas-root.js.map +1 -0
  298. package/src/lib/app-utils/entity-picker-context.d.ts +199 -0
  299. package/src/lib/app-utils/entity-picker-context.js +93 -0
  300. package/src/lib/app-utils/entity-picker-context.js.map +1 -0
  301. package/src/lib/app-utils/error-beacon.d.ts +107 -0
  302. package/src/lib/app-utils/error-beacon.js +296 -0
  303. package/src/lib/app-utils/error-beacon.js.map +1 -0
  304. package/src/lib/app-utils/expand-repeatables.d.ts +172 -0
  305. package/src/lib/app-utils/expand-repeatables.js +299 -0
  306. package/src/lib/app-utils/expand-repeatables.js.map +1 -0
  307. package/src/lib/app-utils/foreign-script.d.ts +82 -0
  308. package/src/lib/app-utils/foreign-script.js +102 -0
  309. package/src/lib/app-utils/foreign-script.js.map +1 -0
  310. package/src/lib/app-utils/form-abuse-ceiling.d.ts +146 -0
  311. package/src/lib/app-utils/form-abuse-ceiling.js +158 -0
  312. package/src/lib/app-utils/form-abuse-ceiling.js.map +1 -0
  313. package/src/lib/app-utils/form-contract.d.ts +198 -0
  314. package/src/lib/app-utils/form-contract.js +282 -0
  315. package/src/lib/app-utils/form-contract.js.map +1 -0
  316. package/src/lib/app-utils/form-dataset-binding.d.ts +50 -0
  317. package/src/lib/app-utils/form-dataset-binding.js +74 -0
  318. package/src/lib/app-utils/form-dataset-binding.js.map +1 -0
  319. package/src/lib/app-utils/forms.d.ts +727 -0
  320. package/src/lib/app-utils/forms.js +777 -0
  321. package/src/lib/app-utils/forms.js.map +1 -0
  322. package/src/lib/app-utils/functions.d.ts +188 -0
  323. package/src/lib/app-utils/functions.js +529 -0
  324. package/src/lib/app-utils/functions.js.map +1 -0
  325. package/src/lib/app-utils/guarded-version-save.d.ts +91 -0
  326. package/src/lib/app-utils/guarded-version-save.js +131 -0
  327. package/src/lib/app-utils/guarded-version-save.js.map +1 -0
  328. package/src/lib/app-utils/health-report.d.ts +1766 -0
  329. package/src/lib/app-utils/health-report.js +2072 -0
  330. package/src/lib/app-utils/health-report.js.map +1 -0
  331. package/src/lib/app-utils/host-naming.d.ts +135 -0
  332. package/src/lib/app-utils/host-naming.js +188 -0
  333. package/src/lib/app-utils/host-naming.js.map +1 -0
  334. package/src/lib/app-utils/host-permissions.d.ts +76 -0
  335. package/src/lib/app-utils/host-permissions.js +119 -0
  336. package/src/lib/app-utils/host-permissions.js.map +1 -0
  337. package/src/lib/app-utils/host-tokens.d.ts +228 -0
  338. package/src/lib/app-utils/host-tokens.js +304 -0
  339. package/src/lib/app-utils/host-tokens.js.map +1 -0
  340. package/src/lib/app-utils/idp-profile.d.ts +83 -0
  341. package/src/lib/app-utils/idp-profile.js +268 -0
  342. package/src/lib/app-utils/idp-profile.js.map +1 -0
  343. package/src/lib/app-utils/image-upload-types.d.ts +49 -0
  344. package/src/lib/app-utils/image-upload-types.js +86 -0
  345. package/src/lib/app-utils/image-upload-types.js.map +1 -0
  346. package/src/lib/app-utils/index.d.ts +18 -0
  347. package/src/lib/app-utils/index.js +23 -0
  348. package/src/lib/app-utils/index.js.map +1 -0
  349. package/src/lib/app-utils/internal-traffic.d.ts +197 -0
  350. package/src/lib/app-utils/internal-traffic.js +288 -0
  351. package/src/lib/app-utils/internal-traffic.js.map +1 -0
  352. package/src/lib/app-utils/lineal-order.d.ts +62 -0
  353. package/src/lib/app-utils/lineal-order.js +65 -0
  354. package/src/lib/app-utils/lineal-order.js.map +1 -0
  355. package/src/lib/app-utils/link-references.d.ts +36 -0
  356. package/src/lib/app-utils/link-references.js +89 -0
  357. package/src/lib/app-utils/link-references.js.map +1 -0
  358. package/src/lib/app-utils/link-target-search-context.d.ts +123 -0
  359. package/src/lib/app-utils/link-target-search-context.js +221 -0
  360. package/src/lib/app-utils/link-target-search-context.js.map +1 -0
  361. package/src/lib/app-utils/list-assignment-policy.d.ts +169 -0
  362. package/src/lib/app-utils/list-assignment-policy.js +170 -0
  363. package/src/lib/app-utils/list-assignment-policy.js.map +1 -0
  364. package/src/lib/app-utils/list-import.d.ts +158 -0
  365. package/src/lib/app-utils/list-import.js +332 -0
  366. package/src/lib/app-utils/list-import.js.map +1 -0
  367. package/src/lib/app-utils/llms-txt.d.ts +104 -0
  368. package/src/lib/app-utils/llms-txt.js +144 -0
  369. package/src/lib/app-utils/llms-txt.js.map +1 -0
  370. package/src/lib/app-utils/local-storage-budget.d.ts +75 -0
  371. package/src/lib/app-utils/local-storage-budget.js +153 -0
  372. package/src/lib/app-utils/local-storage-budget.js.map +1 -0
  373. package/src/lib/app-utils/lockdown.d.ts +698 -0
  374. package/src/lib/app-utils/lockdown.js +929 -0
  375. package/src/lib/app-utils/lockdown.js.map +1 -0
  376. package/src/lib/app-utils/margin-utilization.d.ts +267 -0
  377. package/src/lib/app-utils/margin-utilization.js +351 -0
  378. package/src/lib/app-utils/margin-utilization.js.map +1 -0
  379. package/src/lib/app-utils/markdown-lite.d.ts +297 -0
  380. package/src/lib/app-utils/markdown-lite.js +705 -0
  381. package/src/lib/app-utils/markdown-lite.js.map +1 -0
  382. package/src/lib/app-utils/marketing-consent.d.ts +704 -0
  383. package/src/lib/app-utils/marketing-consent.js +783 -0
  384. package/src/lib/app-utils/marketing-consent.js.map +1 -0
  385. package/src/lib/app-utils/marketplace-listing-visibility.d.ts +120 -0
  386. package/src/lib/app-utils/marketplace-listing-visibility.js +139 -0
  387. package/src/lib/app-utils/marketplace-listing-visibility.js.map +1 -0
  388. package/src/lib/app-utils/marketplace-overrides.d.ts +271 -0
  389. package/src/lib/app-utils/marketplace-overrides.js +462 -0
  390. package/src/lib/app-utils/marketplace-overrides.js.map +1 -0
  391. package/src/lib/app-utils/marketplace-provenance.d.ts +150 -0
  392. package/src/lib/app-utils/marketplace-provenance.js +136 -0
  393. package/src/lib/app-utils/marketplace-provenance.js.map +1 -0
  394. package/src/lib/app-utils/marketplace-theme.d.ts +237 -0
  395. package/src/lib/app-utils/marketplace-theme.js +525 -0
  396. package/src/lib/app-utils/marketplace-theme.js.map +1 -0
  397. package/src/lib/app-utils/marketplace-update-state.d.ts +119 -0
  398. package/src/lib/app-utils/marketplace-update-state.js +132 -0
  399. package/src/lib/app-utils/marketplace-update-state.js.map +1 -0
  400. package/src/lib/app-utils/marketplace-verification.d.ts +95 -0
  401. package/src/lib/app-utils/marketplace-verification.js +81 -0
  402. package/src/lib/app-utils/marketplace-verification.js.map +1 -0
  403. package/src/lib/app-utils/measure-node-map.d.ts +76 -0
  404. package/src/lib/app-utils/measure-node-map.js +111 -0
  405. package/src/lib/app-utils/measure-node-map.js.map +1 -0
  406. package/src/lib/app-utils/media-alt.d.ts +55 -0
  407. package/src/lib/app-utils/media-alt.js +58 -0
  408. package/src/lib/app-utils/media-alt.js.map +1 -0
  409. package/src/lib/app-utils/media-asset-facts.d.ts +125 -0
  410. package/src/lib/app-utils/media-asset-facts.js +467 -0
  411. package/src/lib/app-utils/media-asset-facts.js.map +1 -0
  412. package/src/lib/app-utils/media-cdn-scope.d.ts +65 -0
  413. package/src/lib/app-utils/media-cdn-scope.js +75 -0
  414. package/src/lib/app-utils/media-cdn-scope.js.map +1 -0
  415. package/src/lib/app-utils/media-folders.d.ts +161 -0
  416. package/src/lib/app-utils/media-folders.js +230 -0
  417. package/src/lib/app-utils/media-folders.js.map +1 -0
  418. package/src/lib/app-utils/media-metadata.d.ts +277 -0
  419. package/src/lib/app-utils/media-metadata.js +388 -0
  420. package/src/lib/app-utils/media-metadata.js.map +1 -0
  421. package/src/lib/app-utils/media-picker-context.d.ts +93 -0
  422. package/src/lib/app-utils/media-picker-context.js +26 -0
  423. package/src/lib/app-utils/media-picker-context.js.map +1 -0
  424. package/src/lib/app-utils/media-quarantine.d.ts +247 -0
  425. package/src/lib/app-utils/media-quarantine.js +323 -0
  426. package/src/lib/app-utils/media-quarantine.js.map +1 -0
  427. package/src/lib/app-utils/media-ref.d.ts +661 -0
  428. package/src/lib/app-utils/media-ref.js +1055 -0
  429. package/src/lib/app-utils/media-ref.js.map +1 -0
  430. package/src/lib/app-utils/media-takedown-reach.d.ts +126 -0
  431. package/src/lib/app-utils/media-takedown-reach.js +143 -0
  432. package/src/lib/app-utils/media-takedown-reach.js.map +1 -0
  433. package/src/lib/app-utils/member-email-aliases.d.ts +180 -0
  434. package/src/lib/app-utils/member-email-aliases.js +217 -0
  435. package/src/lib/app-utils/member-email-aliases.js.map +1 -0
  436. package/src/lib/app-utils/merge-node-sx.d.ts +47 -0
  437. package/src/lib/app-utils/merge-node-sx.js +123 -0
  438. package/src/lib/app-utils/merge-node-sx.js.map +1 -0
  439. package/src/lib/app-utils/name-match.d.ts +155 -0
  440. package/src/lib/app-utils/name-match.js +175 -0
  441. package/src/lib/app-utils/name-match.js.map +1 -0
  442. package/src/lib/app-utils/name-search.d.ts +117 -0
  443. package/src/lib/app-utils/name-search.js +145 -0
  444. package/src/lib/app-utils/name-search.js.map +1 -0
  445. package/src/lib/app-utils/node-capabilities.d.ts +85 -0
  446. package/src/lib/app-utils/node-capabilities.js +88 -0
  447. package/src/lib/app-utils/node-capabilities.js.map +1 -0
  448. package/src/lib/app-utils/node-definition-sanitizer.d.ts +164 -0
  449. package/src/lib/app-utils/node-definition-sanitizer.js +402 -0
  450. package/src/lib/app-utils/node-definition-sanitizer.js.map +1 -0
  451. package/src/lib/app-utils/node-identity.d.ts +47 -0
  452. package/src/lib/app-utils/node-identity.js +50 -0
  453. package/src/lib/app-utils/node-identity.js.map +1 -0
  454. package/src/lib/app-utils/node-interactions.d.ts +102 -0
  455. package/src/lib/app-utils/node-interactions.js +192 -0
  456. package/src/lib/app-utils/node-interactions.js.map +1 -0
  457. package/src/lib/app-utils/node-url-policy.d.ts +54 -0
  458. package/src/lib/app-utils/node-url-policy.js +72 -0
  459. package/src/lib/app-utils/node-url-policy.js.map +1 -0
  460. package/src/lib/app-utils/notifications.d.ts +71 -0
  461. package/src/lib/app-utils/notifications.js +101 -0
  462. package/src/lib/app-utils/notifications.js.map +1 -0
  463. package/src/lib/app-utils/onboarding-deep-link.d.ts +120 -0
  464. package/src/lib/app-utils/onboarding-deep-link.js +142 -0
  465. package/src/lib/app-utils/onboarding-deep-link.js.map +1 -0
  466. package/src/lib/app-utils/operator-identity.d.ts +207 -0
  467. package/src/lib/app-utils/operator-identity.js +186 -0
  468. package/src/lib/app-utils/operator-identity.js.map +1 -0
  469. package/src/lib/app-utils/org-billing-doc.d.ts +231 -0
  470. package/src/lib/app-utils/org-billing-doc.js +238 -0
  471. package/src/lib/app-utils/org-billing-doc.js.map +1 -0
  472. package/src/lib/app-utils/org-override-reason.d.ts +100 -0
  473. package/src/lib/app-utils/org-override-reason.js +116 -0
  474. package/src/lib/app-utils/org-override-reason.js.map +1 -0
  475. package/src/lib/app-utils/org-permissions.d.ts +194 -0
  476. package/src/lib/app-utils/org-permissions.js +357 -0
  477. package/src/lib/app-utils/org-permissions.js.map +1 -0
  478. package/src/lib/app-utils/org-roles.d.ts +72 -0
  479. package/src/lib/app-utils/org-roles.js +131 -0
  480. package/src/lib/app-utils/org-roles.js.map +1 -0
  481. package/src/lib/app-utils/organizations.d.ts +407 -0
  482. package/src/lib/app-utils/organizations.js +511 -0
  483. package/src/lib/app-utils/organizations.js.map +1 -0
  484. package/src/lib/app-utils/page-header-actions-context.d.ts +83 -0
  485. package/src/lib/app-utils/page-header-actions-context.js +58 -0
  486. package/src/lib/app-utils/page-header-actions-context.js.map +1 -0
  487. package/src/lib/app-utils/page-header-record-context.d.ts +80 -0
  488. package/src/lib/app-utils/page-header-record-context.js +64 -0
  489. package/src/lib/app-utils/page-header-record-context.js.map +1 -0
  490. package/src/lib/app-utils/page-markdown.d.ts +103 -0
  491. package/src/lib/app-utils/page-markdown.js +385 -0
  492. package/src/lib/app-utils/page-markdown.js.map +1 -0
  493. package/src/lib/app-utils/paid-media-source.d.ts +90 -0
  494. package/src/lib/app-utils/paid-media-source.js +184 -0
  495. package/src/lib/app-utils/paid-media-source.js.map +1 -0
  496. package/src/lib/app-utils/palette-sx.d.ts +34 -0
  497. package/src/lib/app-utils/palette-sx.js +150 -0
  498. package/src/lib/app-utils/palette-sx.js.map +1 -0
  499. package/src/lib/app-utils/password-policy.d.ts +47 -0
  500. package/src/lib/app-utils/password-policy.js +46 -0
  501. package/src/lib/app-utils/password-policy.js.map +1 -0
  502. package/src/lib/app-utils/payments-configured.d.ts +84 -0
  503. package/src/lib/app-utils/payments-configured.js +88 -0
  504. package/src/lib/app-utils/payments-configured.js.map +1 -0
  505. package/src/lib/app-utils/person-erasure.d.ts +126 -0
  506. package/src/lib/app-utils/person-erasure.js +121 -0
  507. package/src/lib/app-utils/person-erasure.js.map +1 -0
  508. package/src/lib/app-utils/person-key.d.ts +32 -0
  509. package/src/lib/app-utils/person-key.js +62 -0
  510. package/src/lib/app-utils/person-key.js.map +1 -0
  511. package/src/lib/app-utils/plan-entitlements.d.ts +2756 -0
  512. package/src/lib/app-utils/plan-entitlements.js +4085 -0
  513. package/src/lib/app-utils/plan-entitlements.js.map +1 -0
  514. package/src/lib/app-utils/platform-ad-conversions.d.ts +122 -0
  515. package/src/lib/app-utils/platform-ad-conversions.js +174 -0
  516. package/src/lib/app-utils/platform-ad-conversions.js.map +1 -0
  517. package/src/lib/app-utils/platform-advertising-tags.d.ts +139 -0
  518. package/src/lib/app-utils/platform-advertising-tags.js +203 -0
  519. package/src/lib/app-utils/platform-advertising-tags.js.map +1 -0
  520. package/src/lib/app-utils/platform-brand.d.ts +151 -0
  521. package/src/lib/app-utils/platform-brand.js +175 -0
  522. package/src/lib/app-utils/platform-brand.js.map +1 -0
  523. package/src/lib/app-utils/platform-consent-default.d.ts +142 -0
  524. package/src/lib/app-utils/platform-consent-default.js +295 -0
  525. package/src/lib/app-utils/platform-consent-default.js.map +1 -0
  526. package/src/lib/app-utils/platform-marketing-consent.d.ts +151 -0
  527. package/src/lib/app-utils/platform-marketing-consent.js +182 -0
  528. package/src/lib/app-utils/platform-marketing-consent.js.map +1 -0
  529. package/src/lib/app-utils/platform-marketing-host.d.ts +33 -0
  530. package/src/lib/app-utils/platform-marketing-host.js +97 -0
  531. package/src/lib/app-utils/platform-marketing-host.js.map +1 -0
  532. package/src/lib/app-utils/platform-visitor-consent.d.ts +252 -0
  533. package/src/lib/app-utils/platform-visitor-consent.js +533 -0
  534. package/src/lib/app-utils/platform-visitor-consent.js.map +1 -0
  535. package/src/lib/app-utils/plugin-api-cross-origin.d.ts +61 -0
  536. package/src/lib/app-utils/plugin-api-cross-origin.js +202 -0
  537. package/src/lib/app-utils/plugin-api-cross-origin.js.map +1 -0
  538. package/src/lib/app-utils/plugin-api-rate-limit.d.ts +192 -0
  539. package/src/lib/app-utils/plugin-api-rate-limit.js +270 -0
  540. package/src/lib/app-utils/plugin-api-rate-limit.js.map +1 -0
  541. package/src/lib/app-utils/plugin-artifact-path.d.ts +32 -0
  542. package/src/lib/app-utils/plugin-artifact-path.js +33 -0
  543. package/src/lib/app-utils/plugin-artifact-path.js.map +1 -0
  544. package/src/lib/app-utils/plugin-bundle-checks.d.ts +148 -0
  545. package/src/lib/app-utils/plugin-bundle-checks.js +989 -0
  546. package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -0
  547. package/src/lib/app-utils/plugin-manifest.d.ts +516 -0
  548. package/src/lib/app-utils/plugin-manifest.js +687 -0
  549. package/src/lib/app-utils/plugin-manifest.js.map +1 -0
  550. package/src/lib/app-utils/published-legal-pages.d.ts +71 -0
  551. package/src/lib/app-utils/published-legal-pages.js +90 -0
  552. package/src/lib/app-utils/published-legal-pages.js.map +1 -0
  553. package/src/lib/app-utils/publisher-agreement.d.ts +254 -0
  554. package/src/lib/app-utils/publisher-agreement.js +274 -0
  555. package/src/lib/app-utils/publisher-agreement.js.map +1 -0
  556. package/src/lib/app-utils/publisher-attestation.d.ts +99 -0
  557. package/src/lib/app-utils/publisher-attestation.js +158 -0
  558. package/src/lib/app-utils/publisher-attestation.js.map +1 -0
  559. package/src/lib/app-utils/recipient-email-render.d.ts +216 -0
  560. package/src/lib/app-utils/recipient-email-render.js +221 -0
  561. package/src/lib/app-utils/recipient-email-render.js.map +1 -0
  562. package/src/lib/app-utils/redispatch-caught-error.d.ts +38 -0
  563. package/src/lib/app-utils/redispatch-caught-error.js +46 -0
  564. package/src/lib/app-utils/redispatch-caught-error.js.map +1 -0
  565. package/src/lib/app-utils/registering-plugin.d.ts +19 -0
  566. package/src/lib/app-utils/registering-plugin.js +38 -0
  567. package/src/lib/app-utils/registering-plugin.js.map +1 -0
  568. package/src/lib/app-utils/release-flags.d.ts +283 -0
  569. package/src/lib/app-utils/release-flags.js +518 -0
  570. package/src/lib/app-utils/release-flags.js.map +1 -0
  571. package/src/lib/app-utils/repair-canvas-nodes.d.ts +130 -0
  572. package/src/lib/app-utils/repair-canvas-nodes.js +298 -0
  573. package/src/lib/app-utils/repair-canvas-nodes.js.map +1 -0
  574. package/src/lib/app-utils/repeat-infringer.d.ts +166 -0
  575. package/src/lib/app-utils/repeat-infringer.js +188 -0
  576. package/src/lib/app-utils/repeat-infringer.js.map +1 -0
  577. package/src/lib/app-utils/repeat-sources.d.ts +115 -0
  578. package/src/lib/app-utils/repeat-sources.js +86 -0
  579. package/src/lib/app-utils/repeat-sources.js.map +1 -0
  580. package/src/lib/app-utils/request-geo.d.ts +114 -0
  581. package/src/lib/app-utils/request-geo.js +247 -0
  582. package/src/lib/app-utils/request-geo.js.map +1 -0
  583. package/src/lib/app-utils/request-ip.d.ts +213 -0
  584. package/src/lib/app-utils/request-ip.js +344 -0
  585. package/src/lib/app-utils/request-ip.js.map +1 -0
  586. package/src/lib/app-utils/resolve-named-tokens.d.ts +26 -0
  587. package/src/lib/app-utils/resolve-named-tokens.js +59 -0
  588. package/src/lib/app-utils/resolve-named-tokens.js.map +1 -0
  589. package/src/lib/app-utils/responsive-typography-sx.d.ts +77 -0
  590. package/src/lib/app-utils/responsive-typography-sx.js +122 -0
  591. package/src/lib/app-utils/responsive-typography-sx.js.map +1 -0
  592. package/src/lib/app-utils/reusable-component-keys.d.ts +135 -0
  593. package/src/lib/app-utils/reusable-component-keys.js +140 -0
  594. package/src/lib/app-utils/reusable-component-keys.js.map +1 -0
  595. package/src/lib/app-utils/reusable-prop-values.d.ts +199 -0
  596. package/src/lib/app-utils/reusable-prop-values.js +494 -0
  597. package/src/lib/app-utils/reusable-prop-values.js.map +1 -0
  598. package/src/lib/app-utils/safe-json-ld.d.ts +29 -0
  599. package/src/lib/app-utils/safe-json-ld.js +37 -0
  600. package/src/lib/app-utils/safe-json-ld.js.map +1 -0
  601. package/src/lib/app-utils/sanitize-svg.d.ts +55 -0
  602. package/src/lib/app-utils/sanitize-svg.js +460 -0
  603. package/src/lib/app-utils/sanitize-svg.js.map +1 -0
  604. package/src/lib/app-utils/scheme-sx.d.ts +84 -0
  605. package/src/lib/app-utils/scheme-sx.js +148 -0
  606. package/src/lib/app-utils/scheme-sx.js.map +1 -0
  607. package/src/lib/app-utils/scope-tokens.d.ts +249 -0
  608. package/src/lib/app-utils/scope-tokens.js +331 -0
  609. package/src/lib/app-utils/scope-tokens.js.map +1 -0
  610. package/src/lib/app-utils/screen-link-context-value.d.ts +57 -0
  611. package/src/lib/app-utils/screen-link-context-value.js +43 -0
  612. package/src/lib/app-utils/screen-link-context-value.js.map +1 -0
  613. package/src/lib/app-utils/screen-link-context.d.ts +185 -0
  614. package/src/lib/app-utils/screen-link-context.js +304 -0
  615. package/src/lib/app-utils/screen-link-context.js.map +1 -0
  616. package/src/lib/app-utils/screen-link-value.d.ts +269 -0
  617. package/src/lib/app-utils/screen-link-value.js +384 -0
  618. package/src/lib/app-utils/screen-link-value.js.map +1 -0
  619. package/src/lib/app-utils/screen-route.d.ts +669 -0
  620. package/src/lib/app-utils/screen-route.js +751 -0
  621. package/src/lib/app-utils/screen-route.js.map +1 -0
  622. package/src/lib/app-utils/screen-seo-fields.d.ts +37 -0
  623. package/src/lib/app-utils/screen-seo-fields.js +42 -0
  624. package/src/lib/app-utils/screen-seo-fields.js.map +1 -0
  625. package/src/lib/app-utils/search-indexing.d.ts +162 -0
  626. package/src/lib/app-utils/search-indexing.js +207 -0
  627. package/src/lib/app-utils/search-indexing.js.map +1 -0
  628. package/src/lib/app-utils/seo-listing-fields.d.ts +70 -0
  629. package/src/lib/app-utils/seo-listing-fields.js +75 -0
  630. package/src/lib/app-utils/seo-listing-fields.js.map +1 -0
  631. package/src/lib/app-utils/seo-locale.d.ts +119 -0
  632. package/src/lib/app-utils/seo-locale.js +133 -0
  633. package/src/lib/app-utils/seo-locale.js.map +1 -0
  634. package/src/lib/app-utils/seo-title-variables.d.ts +149 -0
  635. package/src/lib/app-utils/seo-title-variables.js +190 -0
  636. package/src/lib/app-utils/seo-title-variables.js.map +1 -0
  637. package/src/lib/app-utils/seo-title.d.ts +70 -0
  638. package/src/lib/app-utils/seo-title.js +117 -0
  639. package/src/lib/app-utils/seo-title.js.map +1 -0
  640. package/src/lib/app-utils/server.d.ts +174 -0
  641. package/src/lib/app-utils/server.js +314 -0
  642. package/src/lib/app-utils/server.js.map +1 -0
  643. package/src/lib/app-utils/site-context.d.ts +106 -0
  644. package/src/lib/app-utils/site-context.js +100 -0
  645. package/src/lib/app-utils/site-context.js.map +1 -0
  646. package/src/lib/app-utils/sitemap.d.ts +117 -0
  647. package/src/lib/app-utils/sitemap.js +183 -0
  648. package/src/lib/app-utils/sitemap.js.map +1 -0
  649. package/src/lib/app-utils/social-image.d.ts +112 -0
  650. package/src/lib/app-utils/social-image.js +191 -0
  651. package/src/lib/app-utils/social-image.js.map +1 -0
  652. package/src/lib/app-utils/starter-templates.d.ts +99 -0
  653. package/src/lib/app-utils/starter-templates.js +579 -0
  654. package/src/lib/app-utils/starter-templates.js.map +1 -0
  655. package/src/lib/app-utils/stored-nodes.d.ts +120 -0
  656. package/src/lib/app-utils/stored-nodes.js +174 -0
  657. package/src/lib/app-utils/stored-nodes.js.map +1 -0
  658. package/src/lib/app-utils/strip-undefined.d.ts +59 -0
  659. package/src/lib/app-utils/strip-undefined.js +95 -0
  660. package/src/lib/app-utils/strip-undefined.js.map +1 -0
  661. package/src/lib/app-utils/stripe-deployment-mode.d.ts +47 -0
  662. package/src/lib/app-utils/stripe-deployment-mode.js +70 -0
  663. package/src/lib/app-utils/stripe-deployment-mode.js.map +1 -0
  664. package/src/lib/app-utils/support-tiers.d.ts +108 -0
  665. package/src/lib/app-utils/support-tiers.js +201 -0
  666. package/src/lib/app-utils/support-tiers.js.map +1 -0
  667. package/src/lib/app-utils/timezone-geo-hint.d.ts +36 -0
  668. package/src/lib/app-utils/timezone-geo-hint.js +180 -0
  669. package/src/lib/app-utils/timezone-geo-hint.js.map +1 -0
  670. package/src/lib/app-utils/upload-cors.d.ts +155 -0
  671. package/src/lib/app-utils/upload-cors.js +254 -0
  672. package/src/lib/app-utils/upload-cors.js.map +1 -0
  673. package/src/lib/app-utils/upload-inspection.d.ts +159 -0
  674. package/src/lib/app-utils/upload-inspection.js +539 -0
  675. package/src/lib/app-utils/upload-inspection.js.map +1 -0
  676. package/src/lib/app-utils/url-slug.d.ts +40 -0
  677. package/src/lib/app-utils/url-slug.js +42 -0
  678. package/src/lib/app-utils/url-slug.js.map +1 -0
  679. package/src/lib/app-utils/usage-budget.d.ts +429 -0
  680. package/src/lib/app-utils/usage-budget.js +421 -0
  681. package/src/lib/app-utils/usage-budget.js.map +1 -0
  682. package/src/lib/app-utils/variables.d.ts +96 -0
  683. package/src/lib/app-utils/variables.js +233 -0
  684. package/src/lib/app-utils/variables.js.map +1 -0
  685. package/src/lib/app-utils/video-asset-facts.d.ts +61 -0
  686. package/src/lib/app-utils/video-asset-facts.js +70 -0
  687. package/src/lib/app-utils/video-asset-facts.js.map +1 -0
  688. package/src/lib/app-utils/video-object.d.ts +89 -0
  689. package/src/lib/app-utils/video-object.js +245 -0
  690. package/src/lib/app-utils/video-object.js.map +1 -0
  691. package/src/lib/app-utils/visitor-consent.d.ts +757 -0
  692. package/src/lib/app-utils/visitor-consent.js +981 -0
  693. package/src/lib/app-utils/visitor-consent.js.map +1 -0
  694. package/src/lib/app-utils/visitor-record-ceiling.d.ts +223 -0
  695. package/src/lib/app-utils/visitor-record-ceiling.js +215 -0
  696. package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -0
  697. package/src/lib/app-utils/web-vitals-rum.d.ts +53 -0
  698. package/src/lib/app-utils/web-vitals-rum.js +278 -0
  699. package/src/lib/app-utils/web-vitals-rum.js.map +1 -0
  700. package/src/lib/app-utils/webhook-delivery.d.ts +324 -0
  701. package/src/lib/app-utils/webhook-delivery.js +413 -0
  702. package/src/lib/app-utils/webhook-delivery.js.map +1 -0
  703. package/src/lib/app-utils/where-used.d.ts +57 -0
  704. package/src/lib/app-utils/where-used.js +59 -0
  705. package/src/lib/app-utils/where-used.js.map +1 -0
  706. package/src/lib/app-utils/wistia-embed.d.ts +86 -0
  707. package/src/lib/app-utils/wistia-embed.js +119 -0
  708. package/src/lib/app-utils/wistia-embed.js.map +1 -0
  709. package/src/lib/app-utils/workflows.d.ts +136 -0
  710. package/src/lib/app-utils/workflows.js +346 -0
  711. package/src/lib/app-utils/workflows.js.map +1 -0
  712. package/src/lib/canvas-manager/canvas-manager-tree.d.ts +109 -0
  713. package/src/lib/canvas-manager/canvas-manager-tree.js +139 -0
  714. package/src/lib/canvas-manager/canvas-manager-tree.js.map +1 -0
  715. package/src/lib/canvas-manager/canvas-manager.d.ts +549 -0
  716. package/src/lib/canvas-manager/canvas-manager.js +1311 -0
  717. package/src/lib/canvas-manager/canvas-manager.js.map +1 -0
  718. package/src/lib/canvas-manager/index.d.ts +20 -0
  719. package/src/lib/canvas-manager/index.js +21 -0
  720. package/src/lib/canvas-manager/index.js.map +1 -0
  721. package/src/lib/canvas-manager/stores/index.d.ts +17 -0
  722. package/src/lib/canvas-manager/stores/index.js +18 -0
  723. package/src/lib/canvas-manager/stores/index.js.map +1 -0
  724. package/src/lib/canvas-manager/stores/node.store.d.ts +30 -0
  725. package/src/lib/canvas-manager/stores/node.store.js +45 -0
  726. package/src/lib/canvas-manager/stores/node.store.js.map +1 -0
  727. package/src/lib/components-manager/components-manager.d.ts +56 -0
  728. package/src/lib/components-manager/components-manager.js +191 -0
  729. package/src/lib/components-manager/components-manager.js.map +1 -0
  730. package/src/lib/components-manager/index.d.ts +18 -0
  731. package/src/lib/components-manager/index.js +19 -0
  732. package/src/lib/components-manager/index.js.map +1 -0
  733. package/src/lib/emit-manager/emit-manager.d.ts +56 -0
  734. package/src/lib/emit-manager/emit-manager.js +60 -0
  735. package/src/lib/emit-manager/emit-manager.js.map +1 -0
  736. package/src/lib/emit-manager/index.d.ts +18 -0
  737. package/src/lib/emit-manager/index.js +19 -0
  738. package/src/lib/emit-manager/index.js.map +1 -0
  739. package/src/lib/foundation/constants/_internal.d.ts +18 -0
  740. package/src/lib/foundation/constants/_internal.js +62 -0
  741. package/src/lib/foundation/constants/_internal.js.map +1 -0
  742. package/src/lib/foundation/constants/app.d.ts +23 -0
  743. package/src/lib/foundation/constants/app.js +25 -0
  744. package/src/lib/foundation/constants/app.js.map +1 -0
  745. package/src/lib/foundation/constants/canvas.d.ts +18 -0
  746. package/src/lib/foundation/constants/canvas.js +18 -0
  747. package/src/lib/foundation/constants/canvas.js.map +1 -0
  748. package/src/lib/foundation/constants/components.d.ts +54 -0
  749. package/src/lib/foundation/constants/components.js +61 -0
  750. package/src/lib/foundation/constants/components.js.map +1 -0
  751. package/src/lib/foundation/constants/shared.d.ts +50 -0
  752. package/src/lib/foundation/constants/shared.js +49 -0
  753. package/src/lib/foundation/constants/shared.js.map +1 -0
  754. package/src/lib/foundation/constants/symbol.d.ts +63 -0
  755. package/src/lib/foundation/constants/symbol.js +75 -0
  756. package/src/lib/foundation/constants/symbol.js.map +1 -0
  757. package/src/lib/foundation/definitions/components.types.d.ts +372 -0
  758. package/src/lib/foundation/definitions/components.types.js +180 -0
  759. package/src/lib/foundation/definitions/components.types.js.map +1 -0
  760. package/src/lib/foundation/definitions/contact.types.d.ts +73 -0
  761. package/src/lib/foundation/definitions/contact.types.js +107 -0
  762. package/src/lib/foundation/definitions/contact.types.js.map +1 -0
  763. package/src/lib/foundation/definitions/host-content-collections.d.ts +137 -0
  764. package/src/lib/foundation/definitions/host-content-collections.js +219 -0
  765. package/src/lib/foundation/definitions/host-content-collections.js.map +1 -0
  766. package/src/lib/foundation/definitions/org-billing.types.d.ts +1361 -0
  767. package/src/lib/foundation/definitions/org-billing.types.js +77 -0
  768. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -0
  769. package/src/lib/foundation/definitions/organization.types.d.ts +307 -0
  770. package/src/lib/foundation/definitions/organization.types.js +42 -0
  771. package/src/lib/foundation/definitions/organization.types.js.map +1 -0
  772. package/src/lib/foundation/definitions/platform.types.d.ts +1326 -0
  773. package/src/lib/foundation/definitions/platform.types.js +124 -0
  774. package/src/lib/foundation/definitions/platform.types.js.map +1 -0
  775. package/src/lib/foundation/definitions/property-kinds.d.ts +189 -0
  776. package/src/lib/foundation/definitions/property-kinds.js +565 -0
  777. package/src/lib/foundation/definitions/property-kinds.js.map +1 -0
  778. package/src/lib/foundation/definitions/shared.d.ts +23 -0
  779. package/src/lib/foundation/definitions/shared.js +18 -0
  780. package/src/lib/foundation/definitions/shared.js.map +1 -0
  781. package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +249 -0
  782. package/src/lib/foundation/definitions/write-deny-coverage.util.js +499 -0
  783. package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -0
  784. package/src/lib/foundation/index.d.ts +32 -0
  785. package/src/lib/foundation/index.js +67 -0
  786. package/src/lib/foundation/index.js.map +1 -0
  787. package/src/lib/lifecycle.d.ts +24 -0
  788. package/src/lib/lifecycle.js +39 -0
  789. package/src/lib/lifecycle.js.map +1 -0
  790. package/src/lib/mst/common.d.ts +17 -0
  791. package/src/lib/mst/common.js +23 -0
  792. package/src/lib/mst/common.js.map +1 -0
  793. package/src/lib/mst/index.d.ts +19 -0
  794. package/src/lib/mst/index.js +20 -0
  795. package/src/lib/mst/index.js.map +1 -0
  796. package/src/lib/mst/json.d.ts +22 -0
  797. package/src/lib/mst/json.js +23 -0
  798. package/src/lib/mst/json.js.map +1 -0
  799. package/src/lib/mst/types.d.ts +20 -0
  800. package/src/lib/mst/types.js +18 -0
  801. package/src/lib/mst/types.js.map +1 -0
  802. package/src/lib/plugin-manager/billing-webhook-hooks.d.ts +77 -0
  803. package/src/lib/plugin-manager/billing-webhook-hooks.js +55 -0
  804. package/src/lib/plugin-manager/billing-webhook-hooks.js.map +1 -0
  805. package/src/lib/plugin-manager/commerce-zone-props.d.ts +172 -0
  806. package/src/lib/plugin-manager/commerce-zone-props.js +31 -0
  807. package/src/lib/plugin-manager/commerce-zone-props.js.map +1 -0
  808. package/src/lib/plugin-manager/custom-fields.d.ts +60 -0
  809. package/src/lib/plugin-manager/custom-fields.js +39 -0
  810. package/src/lib/plugin-manager/custom-fields.js.map +1 -0
  811. package/src/lib/plugin-manager/editor-sessions.d.ts +109 -0
  812. package/src/lib/plugin-manager/editor-sessions.js +137 -0
  813. package/src/lib/plugin-manager/editor-sessions.js.map +1 -0
  814. package/src/lib/plugin-manager/enabled-plugins.d.ts +438 -0
  815. package/src/lib/plugin-manager/enabled-plugins.js +403 -0
  816. package/src/lib/plugin-manager/enabled-plugins.js.map +1 -0
  817. package/src/lib/plugin-manager/feature-plugins.d.ts +1314 -0
  818. package/src/lib/plugin-manager/feature-plugins.js +609 -0
  819. package/src/lib/plugin-manager/feature-plugins.js.map +1 -0
  820. package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +11 -0
  821. package/src/lib/plugin-manager/first-party-plugins.generated.js +149 -0
  822. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -0
  823. package/src/lib/plugin-manager/index.d.ts +42 -0
  824. package/src/lib/plugin-manager/index.js +46 -0
  825. package/src/lib/plugin-manager/index.js.map +1 -0
  826. package/src/lib/plugin-manager/install-presets.d.ts +93 -0
  827. package/src/lib/plugin-manager/install-presets.js +86 -0
  828. package/src/lib/plugin-manager/install-presets.js.map +1 -0
  829. package/src/lib/plugin-manager/media-delivery-provider.d.ts +124 -0
  830. package/src/lib/plugin-manager/media-delivery-provider.js +53 -0
  831. package/src/lib/plugin-manager/media-delivery-provider.js.map +1 -0
  832. package/src/lib/plugin-manager/order-fulfilment.d.ts +142 -0
  833. package/src/lib/plugin-manager/order-fulfilment.js +104 -0
  834. package/src/lib/plugin-manager/order-fulfilment.js.map +1 -0
  835. package/src/lib/plugin-manager/plugin-activity-actions.d.ts +122 -0
  836. package/src/lib/plugin-manager/plugin-activity-actions.js +140 -0
  837. package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -0
  838. package/src/lib/plugin-manager/plugin-bridge.d.ts +98 -0
  839. package/src/lib/plugin-manager/plugin-bridge.js +154 -0
  840. package/src/lib/plugin-manager/plugin-bridge.js.map +1 -0
  841. package/src/lib/plugin-manager/plugin-config.d.ts +148 -0
  842. package/src/lib/plugin-manager/plugin-config.js +228 -0
  843. package/src/lib/plugin-manager/plugin-config.js.map +1 -0
  844. package/src/lib/plugin-manager/plugin-console-crons.d.ts +139 -0
  845. package/src/lib/plugin-manager/plugin-console-crons.js +177 -0
  846. package/src/lib/plugin-manager/plugin-console-crons.js.map +1 -0
  847. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +220 -0
  848. package/src/lib/plugin-manager/plugin-contact-capture.js +89 -0
  849. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -0
  850. package/src/lib/plugin-manager/plugin-contributions.d.ts +244 -0
  851. package/src/lib/plugin-manager/plugin-contributions.js +283 -0
  852. package/src/lib/plugin-manager/plugin-contributions.js.map +1 -0
  853. package/src/lib/plugin-manager/plugin-entitlement-keys.d.ts +169 -0
  854. package/src/lib/plugin-manager/plugin-entitlement-keys.js +84 -0
  855. package/src/lib/plugin-manager/plugin-entitlement-keys.js.map +1 -0
  856. package/src/lib/plugin-manager/plugin-entitlements.d.ts +181 -0
  857. package/src/lib/plugin-manager/plugin-entitlements.js +188 -0
  858. package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -0
  859. package/src/lib/plugin-manager/plugin-events.d.ts +145 -0
  860. package/src/lib/plugin-manager/plugin-events.js +79 -0
  861. package/src/lib/plugin-manager/plugin-events.js.map +1 -0
  862. package/src/lib/plugin-manager/plugin-figures.d.ts +251 -0
  863. package/src/lib/plugin-manager/plugin-figures.js +252 -0
  864. package/src/lib/plugin-manager/plugin-figures.js.map +1 -0
  865. package/src/lib/plugin-manager/plugin-host-collections.d.ts +146 -0
  866. package/src/lib/plugin-manager/plugin-host-collections.js +116 -0
  867. package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -0
  868. package/src/lib/plugin-manager/plugin-jobs.d.ts +130 -0
  869. package/src/lib/plugin-manager/plugin-jobs.js +182 -0
  870. package/src/lib/plugin-manager/plugin-jobs.js.map +1 -0
  871. package/src/lib/plugin-manager/plugin-loader.d.ts +86 -0
  872. package/src/lib/plugin-manager/plugin-loader.js +223 -0
  873. package/src/lib/plugin-manager/plugin-loader.js.map +1 -0
  874. package/src/lib/plugin-manager/plugin-manager.d.ts +95 -0
  875. package/src/lib/plugin-manager/plugin-manager.js +343 -0
  876. package/src/lib/plugin-manager/plugin-manager.js.map +1 -0
  877. package/src/lib/plugin-manager/plugin-metered-lines.d.ts +97 -0
  878. package/src/lib/plugin-manager/plugin-metered-lines.js +123 -0
  879. package/src/lib/plugin-manager/plugin-metered-lines.js.map +1 -0
  880. package/src/lib/plugin-manager/plugin-org-erasure.d.ts +52 -0
  881. package/src/lib/plugin-manager/plugin-org-erasure.js +100 -0
  882. package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -0
  883. package/src/lib/plugin-manager/plugin-permissions.d.ts +69 -0
  884. package/src/lib/plugin-manager/plugin-permissions.js +77 -0
  885. package/src/lib/plugin-manager/plugin-permissions.js.map +1 -0
  886. package/src/lib/plugin-manager/plugin-person-erasure.d.ts +58 -0
  887. package/src/lib/plugin-manager/plugin-person-erasure.js +109 -0
  888. package/src/lib/plugin-manager/plugin-person-erasure.js.map +1 -0
  889. package/src/lib/plugin-manager/plugin-record-cards.d.ts +99 -0
  890. package/src/lib/plugin-manager/plugin-record-cards.js +59 -0
  891. package/src/lib/plugin-manager/plugin-record-cards.js.map +1 -0
  892. package/src/lib/plugin-manager/plugin-record-facts.d.ts +111 -0
  893. package/src/lib/plugin-manager/plugin-record-facts.js +51 -0
  894. package/src/lib/plugin-manager/plugin-record-facts.js.map +1 -0
  895. package/src/lib/plugin-manager/plugin-record-routes.d.ts +129 -0
  896. package/src/lib/plugin-manager/plugin-record-routes.js +93 -0
  897. package/src/lib/plugin-manager/plugin-record-routes.js.map +1 -0
  898. package/src/lib/plugin-manager/plugin-record-timeline.d.ts +147 -0
  899. package/src/lib/plugin-manager/plugin-record-timeline.js +39 -0
  900. package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -0
  901. package/src/lib/plugin-manager/plugin-resource-drafts.d.ts +137 -0
  902. package/src/lib/plugin-manager/plugin-resource-drafts.js +51 -0
  903. package/src/lib/plugin-manager/plugin-resource-drafts.js.map +1 -0
  904. package/src/lib/plugin-manager/plugin-services.d.ts +94 -0
  905. package/src/lib/plugin-manager/plugin-services.js +153 -0
  906. package/src/lib/plugin-manager/plugin-services.js.map +1 -0
  907. package/src/lib/plugin-manager/plugin-styles-ui.d.ts +71 -0
  908. package/src/lib/plugin-manager/plugin-styles-ui.js +80 -0
  909. package/src/lib/plugin-manager/plugin-styles-ui.js.map +1 -0
  910. package/src/lib/plugin-manager/plugin-styles.d.ts +176 -0
  911. package/src/lib/plugin-manager/plugin-styles.js +186 -0
  912. package/src/lib/plugin-manager/plugin-styles.js.map +1 -0
  913. package/src/lib/plugin-manager/plugin-subprocessors.d.ts +135 -0
  914. package/src/lib/plugin-manager/plugin-subprocessors.js +129 -0
  915. package/src/lib/plugin-manager/plugin-subprocessors.js.map +1 -0
  916. package/src/lib/plugin-manager/plugin-tax-profile.d.ts +84 -0
  917. package/src/lib/plugin-manager/plugin-tax-profile.js +39 -0
  918. package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -0
  919. package/src/lib/plugin-manager/plugin-user-erasure.d.ts +53 -0
  920. package/src/lib/plugin-manager/plugin-user-erasure.js +84 -0
  921. package/src/lib/plugin-manager/plugin-user-erasure.js.map +1 -0
  922. package/src/lib/plugin-manager/plugin-zones.d.ts +121 -0
  923. package/src/lib/plugin-manager/plugin-zones.js +74 -0
  924. package/src/lib/plugin-manager/plugin-zones.js.map +1 -0
  925. package/src/lib/plugin-manager/realm-plugins.d.ts +112 -0
  926. package/src/lib/plugin-manager/realm-plugins.js +196 -0
  927. package/src/lib/plugin-manager/realm-plugins.js.map +1 -0
  928. package/src/lib/plugin-manager/realm-server.d.ts +54 -0
  929. package/src/lib/plugin-manager/realm-server.js +88 -0
  930. package/src/lib/plugin-manager/realm-server.js.map +1 -0
  931. package/src/lib/plugin-manager/record-zone-props.d.ts +126 -0
  932. package/src/lib/plugin-manager/record-zone-props.js +31 -0
  933. package/src/lib/plugin-manager/record-zone-props.js.map +1 -0
  934. package/src/lib/plugin-manager/site-page-hooks.d.ts +102 -0
  935. package/src/lib/plugin-manager/site-page-hooks.js +114 -0
  936. package/src/lib/plugin-manager/site-page-hooks.js.map +1 -0
  937. package/src/lib/plugin-manager/site-runtime.d.ts +57 -0
  938. package/src/lib/plugin-manager/site-runtime.js +25 -0
  939. package/src/lib/plugin-manager/site-runtime.js.map +1 -0
  940. package/src/lib/plugin-manager/usage-alert-contributors.d.ts +115 -0
  941. package/src/lib/plugin-manager/usage-alert-contributors.js +106 -0
  942. package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -0
  943. package/src/lib/types/ast.d.ts +199 -0
  944. package/src/lib/types/ast.js +20 -0
  945. package/src/lib/types/ast.js.map +1 -0
  946. package/src/lib/types/index.d.ts +20 -0
  947. package/src/lib/types/index.js +21 -0
  948. package/src/lib/types/index.js.map +1 -0
  949. package/src/lib/types/nodes.d.ts +608 -0
  950. package/src/lib/types/nodes.js +28 -0
  951. package/src/lib/types/nodes.js.map +1 -0
  952. package/src/lib/types/routing.d.ts +17 -0
  953. package/src/lib/types/routing.js +18 -0
  954. package/src/lib/types/routing.js.map +1 -0
  955. package/src/lib/types/screen.d.ts +23 -0
  956. package/src/lib/types/screen.js +18 -0
  957. package/src/lib/types/screen.js.map +1 -0
  958. package/src/lib/utils.d.ts +25 -0
  959. package/src/lib/utils.js +42 -0
  960. package/src/lib/utils.js.map +1 -0
  961. package/src/server.d.ts +37 -0
  962. package/src/server.js +55 -0
  963. package/src/server.js.map +1 -0
@@ -0,0 +1,2756 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { AglynOrgBilling, CoreOrgEntitlements, CoreOrgFeatureFlags, OrgDiscount, OrgFeatureFlags, OrgPlan, OrgSeatAddons } from '../foundation';
18
+ import type { PluginOrgFeatures, PluginOrgQuotas } from '../plugin-manager/plugin-entitlement-keys';
19
+ import { type OrgOverrideReasonCode } from './org-override-reason';
20
+ import type { ResolvedBrandingProfile } from './platform-brand';
21
+ /** Sentinel for quotas a plan does not cap; `checkQuota` always allows. */
22
+ export declare const UNLIMITED: number;
23
+ /**
24
+ * How many saved form definitions one site may hold, on every plan that can
25
+ * build them at all.
26
+ *
27
+ * ⛔ **Not a tier lever, and must not become one.** The website-building field
28
+ * does not meter form COUNT: Squarespace, HubSpot and Mailchimp publish no cap
29
+ * whatsoever, Webflow abandoned the lever above its free tier, and of the two
30
+ * that do meter it one is Wix (4/10/25/75) and the other is Jotform
31
+ * (5/25/50/100), a form-first product where a form IS the billable unit. What
32
+ * this platform meters on the forms axis is `formSubmissionsPerMonth`, which
33
+ * is tiered, metered and part of a charged price.
34
+ *
35
+ * So this is an abuse ceiling wearing an entitlement's clothes. It rides
36
+ * `formsPerHost` because that is where `checkQuota` can refuse a create inside
37
+ * the counting transaction, not because the number is sold — and it resolves
38
+ * to the same value on Starter through Enterprise. Only Free differs, and only
39
+ * because the form entity rides `reusableComponents`, which Free lacks.
40
+ *
41
+ * The value clears every published competitor number by 5x and the largest
42
+ * real catalog by far, and it stays STRICTLY below `FORMS_MAX_PER_HOST` — the
43
+ * page size two listing reads use. The gap is deliberate: a catalog can sit
44
+ * above this ceiling when a per-org override is withdrawn, and the two
45
+ * numbers must stay tellable apart so a surface reading the window where it
46
+ * means the ceiling is never accidentally right. `forms.spec.ts` pins both
47
+ * halves.
48
+ *
49
+ * A per-org `entitlements.formsPerHost` override still resolves ahead of this,
50
+ * so a contract can raise or lower it for one org without moving the ceiling
51
+ * everyone else is measured against.
52
+ */
53
+ export declare const FORMS_PER_HOST_CEILING = 500;
54
+ /**
55
+ * Is this quota value "no cap"? (AGL-2482; AGL-2223 is the same class.)
56
+ *
57
+ * WHY A PREDICATE AND NOT `x === UNLIMITED`. `UNLIMITED` is
58
+ * `Number.POSITIVE_INFINITY`, and `JSON.stringify(Infinity)` is **`null`** —
59
+ * so the moment an entitlement crosses a route boundary the sentinel is gone
60
+ * and `=== UNLIMITED` is false for a plan that really is uncapped. Worse, the
61
+ * client then reads `Number(null)`, which is `0`, and `Number.isFinite(0)` is
62
+ * TRUE: the value sails through every guard written to reject a payload that
63
+ * cannot state the terms, and the console renders a cap of zero for the most
64
+ * expensive plan on the price list.
65
+ *
66
+ * So: `Infinity`, `null`, `undefined` and `NaN` all read as unlimited here.
67
+ *
68
+ * THIS IS A BACKSTOP, NOT THE FIX. `null` on the wire genuinely cannot
69
+ * distinguish "unlimited" from "the field is missing", and answering
70
+ * "unlimited" for a missing field is the permissive direction — the same
71
+ * shape as `checkQuota(undefined)` resolving to Free. A route that serialises
72
+ * a quota must therefore send a FINITE number plus an explicit boolean flag
73
+ * (see `/api/billing/storage-overage`), and the client must rebuild the
74
+ * sentinel from the flag with `restoreQuotaLimit` before it does arithmetic.
75
+ * This predicate exists so that a surface which has not been given the flag
76
+ * still renders "Unlimited" instead of `0`, `null` or `Infinity`.
77
+ */
78
+ export declare function isUnlimitedQuota(limit: number | null | undefined): boolean;
79
+ /**
80
+ * A quota cap, written the way every surface in the console must write it:
81
+ * `Unlimited` for an uncapped plan, the number otherwise.
82
+ *
83
+ * ONE implementation on purpose. Six surfaces had grown their own copy of the
84
+ * `limit === UNLIMITED ? 'Unlimited' : String(limit)` line and a seventh
85
+ * category — the ones that interpolated the raw number — shipped
86
+ * "Every site can run null registers on your current plan" and
87
+ * "Screen limit reached (Infinity)" to customers.
88
+ *
89
+ * @param limit The cap. Non-finite, `null` and `undefined` are unlimited.
90
+ * @param unit Appended to a finite number only — `MB`, `registers`. Never
91
+ * appended to `Unlimited`, because "Unlimited MB" reads as a typo.
92
+ */
93
+ export declare function formatQuotaLimit(limit: number | null | undefined, unit?: string): string;
94
+ /**
95
+ * Rebuild the `UNLIMITED` sentinel a JSON round-trip flattened, from the
96
+ * explicit flag the route sent alongside the number.
97
+ *
98
+ * The client half of the wire contract. Once this has run, every comparison
99
+ * downstream is ordinary arithmetic that happens to be correct: `used >
100
+ * Infinity` is false, `Math.max(0, used - Infinity)` is `0`, and nothing has
101
+ * to remember that this particular number might be a `null` in disguise.
102
+ *
103
+ * That mattered more than the display did. `1 > null` is **true**, so a site
104
+ * on an unlimited plan was reported "1 over the limit" while the readout
105
+ * beside it said `1/∞` — a false limit warning on a plan that has no limit.
106
+ *
107
+ * @param value The finite number the route sent (or the `null` it sent for a
108
+ * pre-fix payload).
109
+ * @param unlimited The route's explicit flag. `undefined` means the route has
110
+ * not been taught to send one, and then `value` decides — non-finite reads
111
+ * as unlimited, which is the `isUnlimitedQuota` backstop.
112
+ */
113
+ export declare function restoreQuotaLimit(value: number | null | undefined, unlimited?: boolean): number;
114
+ /**
115
+ * Campaign emails an Enterprise agreement includes per month, before the
116
+ * contract says otherwise.
117
+ *
118
+ * ## Why the one Enterprise quota that is a number
119
+ *
120
+ * Every other Enterprise band is `UNLIMITED` because the thing behind it is
121
+ * infrastructure we already meter and price into the deal — storage, page
122
+ * views, seats. Email is not that. It is a per-message charge from a third
123
+ * party, so an unbounded allowance is an unbounded liability with no meter
124
+ * underneath it and no ceiling a negotiation ever had to name.
125
+ *
126
+ * `UNLIMITED` was also unrepresentable off-process. It is
127
+ * `Number.POSITIVE_INFINITY`, `JSON.stringify(Infinity)` is `null`, and
128
+ * `Number(null)` is `0` — so the most expensive plan on the price list
129
+ * serialised to a cap of ZERO through any route that did not also send the
130
+ * explicit flag `restoreQuotaLimit` rebuilds from. A finite number crosses
131
+ * the wire as itself.
132
+ *
133
+ * ## Why this number
134
+ *
135
+ * Twice Agency's band — the rule every Enterprise fallback follows since the
136
+ * 2026-09-07 pricing decision (see the `enterprise` row of
137
+ * `PLAN_ENTITLEMENTS`) — and inside what the sending platform can actually
138
+ * deliver to one workspace with room to spare. At the shipped constants a
139
+ * workspace's share is 500 messages an hour and a projected month is 720
140
+ * hours, so 360,000 is the ceiling nothing can exceed however it is sold.
141
+ * 260,000 spends 520 of those 720 hours, leaving the rest for bursts, retries
142
+ * and domain warm-up — `email-ceiling-dimensioning.spec.ts` holds that
143
+ * relation, and holds the platform rate still so the gap cannot be closed by
144
+ * quietly raising it.
145
+ *
146
+ * Selling more than that would be selling mail that cannot leave the
147
+ * building: the hourly ceiling DEFERS rather than refuses, so the excess does
148
+ * not bounce, it simply never sends.
149
+ *
150
+ * ## It is a DEFAULT, and a contract raises it
151
+ *
152
+ * `resolveOrgEntitlements` applies a per-org `entitlements.emailSendsPerMonth`
153
+ * override ahead of this, so a deal that buys more email buys it on that org
154
+ * without moving the figure every other agreement is measured against — the
155
+ * same mechanism every numeric entitlement uses.
156
+ */
157
+ export declare const ENTERPRISE_EMAIL_SENDS_PER_MONTH = 260000;
158
+ /**
159
+ * Plan → default entitlements. Versioned with the app so pricing changes are
160
+ * code-reviewed; per-org overrides live on `org.entitlements` and win
161
+ * key-by-key. Tier table aligned to the Tenant Billing & SaaS Plans proposal
162
+ * (AGL-67, 2026-07-07): storage-per-host is media storage; the published
163
+ * total-site-size cap it used to exceed by design was retired in AGL-2133.
164
+ * Metered costs are passed through
165
+ * from Firebase/Vercel at cost × 1.30 separately (AGL-41).
166
+ */
167
+ /** Legacy host-keyed dataset overrides resolved into org keys (AGL-240). */
168
+ type LegacyEntitlementKeys = 'datasetsPerHost' | 'maxDatasetsPerHost';
169
+ /**
170
+ * Keys `OrgEntitlements` still carries so stored documents type-check, but
171
+ * that no plan declares and nothing resolves (AGL-2133). Kept as a named type
172
+ * rather than folded into `LegacyEntitlementKeys`: legacy keys are resolved
173
+ * INTO their replacements, retired ones are dropped, and conflating the two
174
+ * would eventually resolve a retired value into something.
175
+ *
176
+ * `RETIRED_ENTITLEMENT_KEYS` is the runtime half and must list the same keys.
177
+ */
178
+ type RetiredEntitlementKeys = 'totalSiteSizeMb';
179
+ /**
180
+ * Keys stored under `entitlements` that are not quotas at all (AGL-3034): the
181
+ * staff plan comp rides the same staff-owned map, and is read through
182
+ * `readOrgPlanComp` rather than resolved into a limit.
183
+ */
184
+ type NonQuotaEntitlementKeys = 'planComp';
185
+ /**
186
+ * Fully-resolved entitlements: every LIVE CORE quota present, the core
187
+ * features complete, and whatever the plugins declared beside them.
188
+ *
189
+ * `Required` is over the CORE halves on purpose (AGL-3124). It is what keeps
190
+ * a plan table honest — a plan that forgets a platform quota or a platform
191
+ * gate does not compile — and that reasoning does not carry to a plugin's
192
+ * key: a plugin's band comes from its own seat-add-on declaration, and
193
+ * requiring it here would mean every plan row in the core naming every
194
+ * installed plugin's keys, which is the arrangement the seam retires. So the
195
+ * plugin halves join as declared: present once a plugin declares them, and
196
+ * optional, because a workspace without the plugin has no value for them.
197
+ *
198
+ * They join in their MAPPED form (`PluginOrgQuotas`, `PluginOrgFeatures`)
199
+ * because an intersection with a bare interface has no implicit string index
200
+ * signature, and readers that hand these objects to a `Record<string, …>`
201
+ * parameter — the quota line, the digest gates — would stop compiling for a
202
+ * plugin half that is empty.
203
+ */
204
+ export type ResolvedOrgEntitlements = Required<Omit<CoreOrgEntitlements, 'features' | LegacyEntitlementKeys | RetiredEntitlementKeys | NonQuotaEntitlementKeys>> & PluginOrgQuotas & {
205
+ features: Required<CoreOrgFeatureFlags> & PluginOrgFeatures;
206
+ };
207
+ /**
208
+ * Aglyn Assist credits an Enterprise agreement includes per month, before the
209
+ * contract says otherwise.
210
+ *
211
+ * ## Never `UNLIMITED`, and the reason is not stylistic
212
+ *
213
+ * `UNLIMITED` is `Number.POSITIVE_INFINITY`, `JSON.stringify(Infinity)` is
214
+ * `null`, and `Number(null)` is `0`. An unbounded assist band therefore
215
+ * serialises to a band of ZERO through every route that does not also send
216
+ * the explicit flag `restoreQuotaLimit` rebuilds from — which would hand the
217
+ * only customers with a signed contract the one budget that refuses
218
+ * everything. A finite number crosses the wire as itself.
219
+ *
220
+ * The band is also a real liability rather than a capacity we already own.
221
+ * Storage and page views are infrastructure metered into the deal; assist is
222
+ * a per-token charge from a third party, and generative building spends it in
223
+ * units two orders of magnitude apart.
224
+ *
225
+ * ## Why this number
226
+ *
227
+ * Enterprise carries no list price, so the band cannot be sized the way every
228
+ * self-serve tier's is — against what that tier's other cost terms leave out
229
+ * of a known price. It is anchored to the top of the ladder instead: 116,000
230
+ * credits is twice Agency's band, the rule every Enterprise fallback follows
231
+ * since the 2026-09-07 pricing decision (see the `enterprise` row of
232
+ * `PLAN_ENTITLEMENTS`). In cost that is $116 of provider spend a month, or
233
+ * under 9% of any deal priced at or above the self-serve top. A deal below
234
+ * that is sold as Agency, not written as an Enterprise agreement.
235
+ *
236
+ * ## It is a DEFAULT, and a contract raises it
237
+ *
238
+ * `resolveOrgEntitlements` applies a per-org `entitlements.assistCreditsPerMonth`
239
+ * override ahead of this, so a deal that buys more assist buys it on that org
240
+ * without moving the figure every other agreement is measured against — the
241
+ * same mechanism the other contracted bands use.
242
+ */
243
+ export declare const ENTERPRISE_ASSIST_CREDITS_PER_MONTH = 116000;
244
+ /**
245
+ * The Free AI taste (AGL-2925): the assist credits a Free workspace draws
246
+ * on each month, behind a hard wall.
247
+ *
248
+ * ## Why it exists
249
+ *
250
+ * So the AI landing pages can say "generate your first page free" and mean
251
+ * it. Three hundred credits is one or two generated sections or a handful
252
+ * of copy rewrites — enough to see what the assistant does, not enough to
253
+ * build a site on. The band is a WALL: `PLAN_PRICING.free
254
+ * .extraAssistCreditsUsdPer1k` stays `null`, so `assistBandRefuses` answers
255
+ * true and the reservation refuses at 100% with no overage, no switch and no
256
+ * invoice. Nothing about a Free workspace can produce a charge.
257
+ *
258
+ * ## What it costs, and why that is the number
259
+ *
260
+ * A credit is `ASSIST_CREDIT_COST_USD` of provider spend, so the band costs
261
+ * at most **$0.30 per Free workspace per month** — asserted by
262
+ * `apps/console/specs/tier-margin-floor.spec.ts`. It is the one AI band with
263
+ * no invoice behind it, which is why the same issue closes every multiplier
264
+ * a script could use before the flag flips:
265
+ *
266
+ * - it is metered per ACCOUNT as well as per workspace, so the three free
267
+ * workspaces an account may hold (AGL-2265) share one 300-credit
268
+ * allowance rather than tripling it (`reserveAssistMessage`);
269
+ * - a fresh account waits `AI_FREE_MIN_ACCOUNT_AGE_HOURS` before it can
270
+ * spend, and every free request is bounded per IP, per uid and per
271
+ * account per day;
272
+ * - a platform-wide daily ceiling on free spend pauses the taste for
273
+ * everyone until the UTC day rolls, paid workspaces untouched.
274
+ *
275
+ * The account allowance reads THIS constant rather than the workspace's
276
+ * resolved band, because a staff override that widens one workspace's band
277
+ * is a decision about that workspace, not about how much one person may
278
+ * draw across all of theirs.
279
+ */
280
+ export declare const FREE_AI_TASTE_CREDITS_PER_MONTH = 300;
281
+ export declare const PLAN_ENTITLEMENTS: Record<OrgPlan, ResolvedOrgEntitlements>;
282
+ /**
283
+ * The tiers a customer can buy themselves — every plan EXCEPT `enterprise`
284
+ * (AGL-1118), which is priced per deal and provisioned by staff (AGL-1110).
285
+ * Any surface that offers plans for sale (the console plan-cards grid, the
286
+ * marketing pricing table, the plan-change dialog) MUST iterate this rather
287
+ * than `Object.keys(PLAN_ENTITLEMENTS)`, so a custom-priced tier can never
288
+ * show a self-serve Upgrade button or a $0 headline price.
289
+ */
290
+ export declare const SELF_SERVE_PLANS: OrgPlan[];
291
+ /**
292
+ * Whether the plan has no list price and is quoted per deal (AGL-1118).
293
+ * `PLAN_PRICING` still carries a row for it — the record is exhaustive by
294
+ * type — but every figure there is 0/null and means "no list price", NOT
295
+ * "free". Revenue for such an org comes from `subscription.customMonthlyUsd`.
296
+ */
297
+ export declare function isCustomPricedPlan(plan: OrgPlan | undefined | null): boolean;
298
+ /** Human plan names — the ONE source every plan badge/label reads. */
299
+ export declare const PLAN_LABELS: Record<OrgPlan, string>;
300
+ /**
301
+ * Event Calendar add-on (AGL-145): first-party, Aglyn-supported, $9/mo
302
+ * **per organization** — cost×1.3 floor honored (mostly Firestore reads).
303
+ * Enabled via the `eventCalendar` entitlement override at purchase.
304
+ *
305
+ * NOT per host, despite what this comment said until AGL-1279. AGL-145
306
+ * shipped it per host; AGL-526 then made add-ons self-serve and capped this
307
+ * one at `return 1` in `app/api/billing/addons/route.ts`, and
308
+ * `resolveOrgEntitlements` flips a single org-level `features.eventCalendar`
309
+ * boolean from any quantity >= 1. One purchase covers every host in the org.
310
+ * The marketing copy on /pricing is written against that behaviour.
311
+ */
312
+ export declare const EVENT_CALENDAR_ADDON_MONTHLY_USD = 9;
313
+ /**
314
+ * POS Pro register add-on (AGL-329): $89/mo per extra register/location
315
+ * (Shopify POS Pro parity). Purchased add-ons land as a per-org
316
+ * `posRegisters` entitlement override, which
317
+ * `resolveOrgEntitlements` already applies over the plan default.
318
+ */
319
+ export declare const POS_REGISTER_ADDON_MONTHLY_USD = 89;
320
+ /**
321
+ * The Aglyn AI add-on's credit band (AGL-2896): the credits a month the
322
+ * add-on ADDS to the plan's `assistCreditsPerMonth` when
323
+ * `seatAddons.aiAddon` is set. One pool and one meter — the add-on widens
324
+ * the band the assist meter already draws on rather than opening a second
325
+ * one, so `resolveAssistCreditBudget`, the overage price and the hard-cap
326
+ * switch all see one number.
327
+ *
328
+ * ## Sized against the add-on's own price
329
+ *
330
+ * A credit is `ASSIST_CREDIT_COST_USD` of provider spend, so each band is a
331
+ * cost figure: 1,000 credits are $1.00. Every band here costs at most 50% of
332
+ * `PlanPricing.aiAddonMonthlyUsd` at full consumption — Starter $4 of $9,
333
+ * Pro $9 of $19, Business $19 of $39, Scale $34 of $69, Advanced $49 of
334
+ * $99, Agency $149 of $299 — which is the same line-margin floor
335
+ * (`ASSIST_CREDIT_MIN_MARGIN_PCT`) every retail assist rate clears.
336
+ * `tier-margin-floor.spec.ts` holds the whole tier non-negative with the
337
+ * add-on band at 100% and the add-on's revenue counted, and pins each band's
338
+ * share of its price.
339
+ *
340
+ * ## Free and Enterprise
341
+ *
342
+ * Free is 0: the plan sells no add-on (`aiAddonMonthlyUsd` is null), so a
343
+ * quantity written onto a Free org adds nothing. Enterprise is finite at
344
+ * twice Agency's, the rule every Enterprise fallback follows since the
345
+ * 2026-09-07 decision (AGL-2654) — never `UNLIMITED`, for the reason
346
+ * `ENTERPRISE_ASSIST_CREDITS_PER_MONTH` gives: an unbounded band reads back
347
+ * as zero off the wire. Enterprise carries `aiGenerative` in the agreement
348
+ * and its price is null too, so this figure is what a contract that names
349
+ * the add-on starts from before its own number is written.
350
+ */
351
+ export declare const AI_ADDON_CREDITS_PER_MONTH: Record<OrgPlan, number>;
352
+ /**
353
+ * The generative add-on's name as a person sees it (AGL-2896): the brand,
354
+ * then "AI". Read from the platform brand by default and from the org's
355
+ * resolved `productName` where the surface has one, for the reason every
356
+ * rendered product name is: a self-host operator renames the product by
357
+ * configuration, and a white-label org must not see ours.
358
+ */
359
+ export declare const aiAddonName: (brand?: string) => string;
360
+ /**
361
+ * Purchase ceilings for the two add-on kinds that have NO per-plan hard max
362
+ * (AGL-1738). They live here, beside the bands and the resolver they bound,
363
+ * because they ARE the bound — and until now they were two private literals
364
+ * in `app/api/billing/addons/route.ts`, seventeen hundred lines and one
365
+ * project away from the `+` they cap.
366
+ *
367
+ * That distance is why AGL-1738 read `resolveOrgEntitlements` as unbounded
368
+ * and proposed a `maxPosRegistersPerHost` clamp. Do not add one. Seats and
369
+ * datasets clamp at `checkSeatQuota` / `checkDatasetQuota` because their
370
+ * plans genuinely refuse to sell past a band (`upgradeRequired` flips and
371
+ * the answer is a plan change). Registers and extra sites are sold flat
372
+ * instead: `addonMax()` will sell an org up to these quantities on ANY plan
373
+ * carrying the feature, so a band-shaped clamp in the resolver would take
374
+ * back registers the merchant is being invoiced $89/mo each for — the
375
+ * expensive direction of a wrong clamp, and the reason the add stays open.
376
+ *
377
+ * The self-serve route is the only path that writes `seatAddons` from a
378
+ * purchase, so these are the real ceilings: a plan's band plus at most
379
+ * `POS_REGISTERS_ADDON_MAX` registers, or plus `EXTRA_HOSTS_ADDON_MAX` sites.
380
+ * Any console surface that windows a head-count must clear the SUM, not the
381
+ * band (`registers-card.component.tsx` still windows at 25; AGL-1738).
382
+ */
383
+ export declare const EXTRA_HOSTS_ADDON_MAX = 100;
384
+ /** @see EXTRA_HOSTS_ADDON_MAX — the flat register ceiling, per org. */
385
+ export declare const POS_REGISTERS_ADDON_MAX = 50;
386
+ export interface PlanPricing {
387
+ /** Flat monthly base price in USD (month-to-month billing). */
388
+ basePriceMonthlyUsd: number;
389
+ /**
390
+ * Effective per-month price when billed annually (AGL-278): the
391
+ * Squarespace/Shopify-parity headline number. Charged as ×12 up front.
392
+ */
393
+ basePriceAnnualMonthlyUsd: number;
394
+ /**
395
+ * Monthly price per host beyond `hostLimit` (AGL-68); null when the plan
396
+ * cannot buy extra hosts.
397
+ *
398
+ * ## It is not a discount ladder, because what it GRANTS is not constant
399
+ *
400
+ * `storagePerHostMb` and `formSubmissionsPerMonth` are per-HOST
401
+ * entitlements, and `meteredIncludedAllowance` expands both by `hostLimit`
402
+ * — so an extra host does not add a slot, it adds that tier's per-host
403
+ * bands to the org's included allowance. A tier that includes more per host
404
+ * therefore hands over more capacity for the same purchase.
405
+ *
406
+ * A ladder that stepped down with the tier inverted that: the tiers
407
+ * granting the most charged the least, and the two largest ran at 24% and
408
+ * 6% margin on a line sold as an add-on.
409
+ *
410
+ * ## It sits above every rung's step rate, or the rungs stop meaning anything
411
+ *
412
+ * The shape is a flat $20 on every plan, Starter included. A flat $8 above
413
+ * Starter, with no cap on how many hosts a plan may add, priced the
414
+ * marginal host below every step of the base ladder, which costs $8.57 to
415
+ * $16 per included host on annual prices and $12 to $22 on monthly: ten
416
+ * Business sites plus fifteen extras came to $219 against Advanced's $299,
417
+ * and plus ninety extras to $819 against Agency's $1,049, so on host count
418
+ * alone no plan was ever worth upgrading to. $20 clears the steepest step on
419
+ * both intervals: extra hosts are how a plan takes a few more sites, and
420
+ * the next plan is how it takes many more (2026-09-20).
421
+ */
422
+ extraHostMonthlyUsd: number | null;
423
+ /**
424
+ * Monthly price per org-manager seat beyond `managersPerOrg`
425
+ * (AGL-112); null when the plan cannot buy extra seats.
426
+ */
427
+ extraSeatMonthlyUsd: number | null;
428
+ /**
429
+ * Monthly price per site-collaborator seat beyond `membersPerHost`
430
+ * (AGL-112, renamed AGL-888): per-site console collaborators
431
+ * (`hosts/{id}/members`, viewer/editor/admin) — never end-user member
432
+ * accounts, which are unlimited on every plan. Null when the plan sells
433
+ * no extra seats. Persisted/external keys keep the legacy "member"
434
+ * spelling (`seatAddons.members`, `STRIPE_PRICE_*_EXTRA_MEMBER`).
435
+ */
436
+ extraCollaboratorMonthlyUsd: number | null;
437
+ /**
438
+ * Monthly price per org dataset beyond `datasetsPerOrg` (AGL-132/240);
439
+ * null when the plan cannot buy extra datasets.
440
+ */
441
+ extraDatasetMonthlyUsd: number | null;
442
+ /**
443
+ * Retail overage per GB-month of dataset storage beyond
444
+ * `dataStorageMbPerOrg` (AGL-240); null when the plan hard-blocks at the
445
+ * included size instead of metering.
446
+ *
447
+ * ⛔ **NOT the metered storage pass-through.** `/pricing` publishes two
448
+ * per-GB-month figures and they are different products: the pass-through
449
+ * is `METERED_BILLED_RATES_USD.storagePerGbMonth` at $0.0338, media bytes
450
+ * on GCS at cost + 30%; this is Firestore-backed DATASET bytes, an order
451
+ * of magnitude pricier to store, sold as a retail line. Confusing the two
452
+ * is the easiest way to corrupt a correct price, in either direction.
453
+ *
454
+ * Priced against `ORG_COGS_UNIT_RATES_USD.dataStoragePerGbMonth` ($0.18)
455
+ * at the 50% retail floor. It sat at $0.25 — a 28% line margin, close
456
+ * enough to the pass-through's 23% that the two looked like the same kind
457
+ * of number while being sold as opposites.
458
+ */
459
+ extraDataGbMonthlyUsd: number | null;
460
+ /**
461
+ * Metered overage per 1,000 customer REST API requests beyond
462
+ * `apiRequestsPerMonth` (AGL-634). API access starts at Business and every
463
+ * tier above it carries it, so Free, Starter and Pro are null (no API to
464
+ * meter).
465
+ */
466
+ extraApiRequestsUsdPer1k: number | null;
467
+ /**
468
+ * Retail overage per 1,000 Aglyn Assist credits beyond
469
+ * `assistCreditsPerMonth`.
470
+ *
471
+ * ## The rate is a multiplier on our own cost, and reads as one
472
+ *
473
+ * A credit is defined as a fixed quantity of provider spend
474
+ * (`ASSIST_CREDIT_COST_USD`), so 1,000 credits cost us exactly $1.00 and
475
+ * this figure IS the multiplier: $3.00 per 1,000 is cost x3, $2.00 is
476
+ * cost x2. That is the property worth having, because the alternative —
477
+ * a rate per message — prices a question and a generated screen the same
478
+ * when they differ by two orders of magnitude.
479
+ *
480
+ * ## The floor, and why the ladder stops descending
481
+ *
482
+ * Every retail line carries at least a 50% margin
483
+ * (`ASSIST_CREDIT_MIN_MARGIN_PCT`), which at $1.00 of cost per 1,000 is a
484
+ * floor of $2.00 per 1,000 — cost x2. The ladder steps down with the tier
485
+ * the way contacts and API requests do, and since AGL-3203 it begins on
486
+ * Starter: $3.00 on Starter and Pro together, then $2.75 -> $2.50 ->
487
+ * $2.25, stopping at the floor on Agency rather than running past it.
488
+ * Starter joins at Pro's figure rather than stepping above it, because the
489
+ * ladder descends with the tier and Starter is the rung below Pro.
490
+ *
491
+ * ## Where it is null, and why that is not "free overage"
492
+ *
493
+ * Null on Free because its band is a WALL by decision (AGL-2925): the
494
+ * 300-credit taste refuses at 100% with nothing sold past it, and a rate
495
+ * here would be a charge on a plan that must never produce one. Null on
496
+ * Enterprise, where every rate is the "not for sale" sentinel and the
497
+ * terms are contractual.
498
+ *
499
+ * Those two are the only nulls. Every self-serve PAID plan now includes a
500
+ * positive assist band and therefore carries a rate, Starter included —
501
+ * a finite band with no rate beside it is usage past a bound that is
502
+ * silently free, which `plan-entitlements.spec.ts` forbids.
503
+ *
504
+ * ## What the rate decides at the gate (AGL-2653)
505
+ *
506
+ * A plan WITH a rate sells past its band by default: `reserveAssistMessage`
507
+ * keeps reserving and `report-usage` bills the excess here, unless the org
508
+ * has switched on `assistOverage.hardCap` to be refused at the band
509
+ * instead. A plan with NO rate has nothing to sell the excess at, so its
510
+ * band stays the wall it always was, switch or no switch — see
511
+ * `assistBandRefuses`. Unlike email, a null here strands nothing: there is
512
+ * no unrefusable assist traffic to absorb.
513
+ */
514
+ extraAssistCreditsUsdPer1k: number | null;
515
+ /**
516
+ * The Aglyn AI add-on, flat per organization per month (AGL-2896); null
517
+ * when the plan does not sell it.
518
+ *
519
+ * ## What it buys
520
+ *
521
+ * `features.aiGenerative` — generative building and automation, the rung
522
+ * no self-serve tier includes — plus `AI_ADDON_CREDITS_PER_MONTH[plan]`
523
+ * added to the plan's assist band. It also switches `aiAssist` on, so on
524
+ * Starter it brings the two rungs the tier does not carry — the copy
525
+ * assist and the generative one — on top of the 750 credits the plan
526
+ * already includes (AGL-3203). It ADDS to that band; it does not replace
527
+ * it, on Starter or on any tier.
528
+ *
529
+ * ## Where the ladder sits
530
+ *
531
+ * Priced as an uplift on the plan rather than on its own cost: the field
532
+ * charges roughly 30% of the base price for an AI tier (Zylo's 2025
533
+ * survey puts the AI uplift at 20–37% across SaaS; aissist.io's benchmark
534
+ * and the Framer, Webflow and HubSpot credit add-ons sit in the same
535
+ * band), and the share DESCENDS with the tier — 36% of Starter's $25, 34%
536
+ * of Pro's, 28% of Business's, 28% of Scale's, 25% of Advanced's, 23% of
537
+ * Agency's — because the larger the plan the more of its price is already
538
+ * carrying the bands the assistant builds against.
539
+ *
540
+ * Annual is x12 of this with no discount, as every other add-on is
541
+ * (`orgListPriceMonthlyUsd`). The band it funds costs at most 50% of it in
542
+ * provider spend, the `ASSIST_CREDIT_MIN_MARGIN_PCT` floor every retail
543
+ * assist figure holds.
544
+ *
545
+ * Null on Free, which sells no add-on of any kind, and on Enterprise,
546
+ * where every figure is the "not for sale" sentinel and generative
547
+ * building is in the agreement.
548
+ */
549
+ aiAddonMonthlyUsd: number | null;
550
+ /**
551
+ * Metered overage per 1,000 CRM records beyond `contactsPerHost` (AGL-890,
552
+ * widened to contacts + companies + deals in AGL-2611): audience bands,
553
+ * Ghost-style. Paid plans meter (a growing audience is never dropped);
554
+ * free is null and hard-bands at the included count. The key keeps its
555
+ * name because it is a persisted Stripe-side vocabulary and a locked
556
+ * price; the band it prices was widened, the rate was not.
557
+ */
558
+ extraContactsUsdPer1k: number | null;
559
+ /**
560
+ * Retail overage per 1,000 emails beyond `emailSendsPerMonth`.
561
+ *
562
+ * ## What it prices, which is not what the cap refuses
563
+ *
564
+ * The cap is enforced against CAMPAIGN sends alone and refuses them at the
565
+ * band — a campaign is discretionary, so the plan is allowed to say no.
566
+ * Transactional mail is never refused at any tier, so an org finishes the
567
+ * month above its band whenever receipts, invites, booking reminders and
568
+ * workflow notifications carry it there. That excess is real provider spend
569
+ * that no gate could have stopped, and this is the rate it bills at.
570
+ *
571
+ * ## Retail, NOT the infrastructure pass-through
572
+ *
573
+ * `meteredInfraPassThrough` covers exactly three meters — storage, page
574
+ * views, form submissions — and the published term for those is "at cost +
575
+ * 30%", which makes the figure in `METERED_BILLED_RATES_USD` the claim
576
+ * itself. Email is not in that set and this rate is not derived from
577
+ * `METERED_MARKUP`: it is a price, set beside `extraContactsUsdPer1k` and
578
+ * `extraApiRequestsUsdPer1k`, and it descends with the tier the way both of
579
+ * those do. Describing it as "at cost" anywhere would make the pass-through
580
+ * sentence false about three meters that really are.
581
+ *
582
+ * Our own per-email cost is `ORG_COGS_UNIT_RATES_USD.perEmailSend`, and it
583
+ * is a cost-model input rather than anything a customer is quoted.
584
+ *
585
+ * ## The floor, and why the ladder stops descending
586
+ *
587
+ * Every retail line must carry at least a 50% margin, and at a cost of
588
+ * $0.90 per 1,000 that floor is $1.80. The ladder still steps down with
589
+ * volume the way contacts and API requests do — a larger plan pays less per
590
+ * message — it simply stops at the floor instead of running past it, which
591
+ * is what the contacts ladder did when it reached $0.25 against a $0.20
592
+ * cost.
593
+ *
594
+ * Null wherever the plan has no email band to be over, and null on
595
+ * enterprise, where every rate is the "not for sale" sentinel and the terms
596
+ * are contractual. Free and Starter are the two banded at 0: campaign email
597
+ * begins at Pro, and `emailSendsOverage` yields 0 against a non-positive
598
+ * band, so a rate on either would advertise a fee that can never be
599
+ * computed — the same defect as a rate beside an UNLIMITED band.
600
+ *
601
+ * Every tier whose band is POSITIVE carries one, Pro upward. A finite band
602
+ * with no rate is silently free past the band, and since transactional mail
603
+ * cannot be refused at any tier that would be unbounded absorbed spend
604
+ * rather than "no overage".
605
+ *
606
+ * The Starter null therefore leaves that tier's transactional mail — the
607
+ * only mail it can send — unbilled at any volume. Bounded in practice by
608
+ * `hostLimit: 1` and a 1,000-contact CRM, and the alternative prices a
609
+ * password reset, but it is the one place this axis absorbs cost by design.
610
+ */
611
+ extraEmailSendsUsdPer1k: number | null;
612
+ /**
613
+ * Whether the plan carries the metered infrastructure pass-through
614
+ * (AGL-41, turned on in AGL-1280): media storage, bandwidth (page views)
615
+ * and form submissions BEYOND the plan's included bands, billed at
616
+ * cost × `METERED_MARKUP`. The rates are platform-wide, not per-plan —
617
+ * this flag only says whether a plan is subject to them at all, which is
618
+ * why it is a boolean rather than another `extra*` rate.
619
+ *
620
+ * Free is `false` because there is no subscription to hang a metered item
621
+ * on; its bands stay hard caps, which is also what stops "metered means
622
+ * you're never cut off" from becoming unlimited free capacity.
623
+ *
624
+ * Enterprise is `false` because every band it has is `UNLIMITED` and its
625
+ * price is negotiated (`isCustomPricedPlan`) — there is no published
626
+ * pass-through to apply, and an overage can never arise anyway.
627
+ */
628
+ meteredInfraPassThrough: boolean;
629
+ }
630
+ /**
631
+ * Flat subscription pricing per tier (AGL-68). Kept beside the entitlements
632
+ * so price changes ride the same review path; Stripe price ids map to plans
633
+ * via `STRIPE_PRICE_*` env vars on the billing API routes.
634
+ */
635
+ export declare const PLAN_PRICING: Record<OrgPlan, PlanPricing>;
636
+ /**
637
+ * The prices the org's usage and add-ons are sold at (AGL-3034): its
638
+ * effective plan's row, or that row with nothing for sale when a staff comp
639
+ * is what grants the plan — see `compPricing`.
640
+ *
641
+ * EVERY reader that decides whether something past a band is sold, or what a
642
+ * purchasable seat costs, reads this rather than indexing `PLAN_PRICING` by
643
+ * the effective plan. Indexing it directly is how a comp would be quoted a
644
+ * rate nobody can be invoiced at.
645
+ */
646
+ export declare function resolvePlanPricing(org: Partial<AglynOrgBilling> | null | undefined): PlanPricing;
647
+ /**
648
+ * What the org's subscription contributes to deciding its plan (AGL-3034).
649
+ *
650
+ * - `live` — a status is mirrored and it is not one this module reads as
651
+ * dead. The subscription decides the plan. `past_due` is live (dunning
652
+ * grace), and so is a status this build does not recognize, for the reason
653
+ * `DEAD_SUBSCRIPTION_STATUSES` gives: an unrecognized status must not take
654
+ * away what a paying workspace bought, and handing the plan to a comp would
655
+ * do exactly that.
656
+ * - `dead` — the status is in `DEAD_SUBSCRIPTION_STATUSES`. A stored paid
657
+ * plan grants nothing (AGL-247).
658
+ * - `none` — no status at all: the workspace has never been subscribed, or
659
+ * predates the mirror.
660
+ *
661
+ * `dead` and `none` are the only states a staff comp applies in.
662
+ */
663
+ export type OrgSubscriptionState = 'live' | 'dead' | 'none';
664
+ export declare function orgSubscriptionState(org: Partial<AglynOrgBilling> | null | undefined): OrgSubscriptionState;
665
+ /** A staff plan comp, as every reader receives it (AGL-3034). */
666
+ export interface ResolvedOrgPlanComp {
667
+ /** The plan granted — always a paid plan this build knows. */
668
+ plan: OrgPlan;
669
+ /**
670
+ * Whether the comp lifts every cap (AGL-3049) — see `isUncappedPlanComp`.
671
+ * `false` for anything but a stored `true`.
672
+ */
673
+ uncapped: boolean;
674
+ /** `null` when the stored code is not one of the override set's. */
675
+ reason: OrgOverrideReasonCode | null;
676
+ note: string | null;
677
+ grantedBy: string | null;
678
+ /** ISO instant, or `null` when the stored stamp does not read back. */
679
+ grantedAt: string | null;
680
+ }
681
+ /**
682
+ * The org's STORED comp (`entitlements.planComp`), whether or not it is in
683
+ * force, or `null` when there is none (AGL-3034).
684
+ *
685
+ * Strict about the one field that grants anything: `plan` must be a paid plan
686
+ * this build knows. A comp of `free` grants nothing and an unknown plan could
687
+ * grant anything, so both read as no comp at all — the org then resolves
688
+ * exactly as it would without one, which is the direction a malformed grant
689
+ * must fail in. The descriptive fields are narrowed and never block the plan:
690
+ * a comp whose note or stamp did not read back is still the comp staff wrote.
691
+ *
692
+ * `uncapped` is strict in the same direction (AGL-3049): it lifts every cap,
693
+ * so only a literal `true` does. A `"true"`, a `1` or a map written by hand
694
+ * reads as a capped comp — the plan still stands, and its bands stay walls.
695
+ */
696
+ export declare function readOrgPlanComp(org: Partial<AglynOrgBilling> | null | undefined): ResolvedOrgPlanComp | null;
697
+ /**
698
+ * The comp IN FORCE: the stored comp while no subscription decides the plan,
699
+ * and `null` otherwise (AGL-3034).
700
+ *
701
+ * A live subscription always wins. The comp is not removed by one starting —
702
+ * nothing but the staff override writes it — so it waits, dormant, and
703
+ * applies again if that subscription ends. Staff views say so.
704
+ */
705
+ export declare function resolvePlanComp(org: Partial<AglynOrgBilling> | null | undefined): ResolvedOrgPlanComp | null;
706
+ /**
707
+ * Whether an UNCAPPED comp is in force (AGL-3049): every band and quota the
708
+ * org resolves reads as `UNLIMITED`, whatever its plan row and its per-org
709
+ * overrides say — see `withEveryCapLifted`.
710
+ *
711
+ * In force only, like the comp itself. A live subscription decides the plan
712
+ * and its bands, so a dormant uncapped comp lifts nothing, and a canceled one
713
+ * lifts every cap again the moment the subscription ends.
714
+ *
715
+ * What it does NOT lift is anything that is not a plan band: the fee
716
+ * percentages (`PRICE_ENTITLEMENT_KEYS`) are prices, and the platform's own
717
+ * guards live outside the entitlements map and are not the plan's to give
718
+ * away — the form-submission and bandwidth abuse ceilings (which read an
719
+ * unlimited band as `FORM_ABUSE_CEILING_UNLIMITED` and
720
+ * `BANDWIDTH_ABUSE_CEILING_UNLIMITED`), the flat per-site caps on webhooks,
721
+ * actions and collection entries, the email send pacing, the assist runaway
722
+ * message cap, an operator's explicit spend ceiling, and staff pauses and
723
+ * lockdowns. It never sells anything either: the pricing stays the comp's,
724
+ * with every rate withheld, so an unbounded band is still billed nothing.
725
+ *
726
+ * The lifted value is `UNLIMITED` in process and nowhere else. Every route
727
+ * that sends a band off-process sends a finite number with an explicit flag,
728
+ * or `null` with a documented meaning, because `JSON.stringify(Infinity)` is
729
+ * `null` — and the comp itself is stored and sent as a boolean.
730
+ */
731
+ export declare function isUncappedPlanComp(org: Partial<AglynOrgBilling> | null | undefined): boolean;
732
+ /**
733
+ * What decided the org's effective plan (AGL-3034):
734
+ *
735
+ * - `subscription` — a live subscription; the stored plan is its mirror.
736
+ * - `comp` — a staff comp, on a dead or absent subscription.
737
+ * - `lapsed` — a dead subscription and no comp: Free.
738
+ * - `stored-plan` — no subscription and no comp, and a paid plan stored
739
+ * directly, the way staff granted plans before comps.
740
+ * - `no-plan` — no subscription, no comp, no paid plan: Free.
741
+ */
742
+ export type OrgPlanDecidedBy = 'subscription' | 'comp' | 'lapsed' | 'stored-plan' | 'no-plan';
743
+ /** Everything a staff surface needs to say how an org's plan resolves. */
744
+ export interface OrgPlanDescription {
745
+ effectivePlan: OrgPlan;
746
+ /** `org.plan` when it names a plan this build knows, else `null`. */
747
+ storedPlan: OrgPlan | null;
748
+ subscriptionStatus: string | null;
749
+ subscription: OrgSubscriptionState;
750
+ /** The STORED comp — in force or dormant; see `compInForce`. */
751
+ comp: ResolvedOrgPlanComp | null;
752
+ compInForce: boolean;
753
+ /**
754
+ * An uncapped comp is IN FORCE, so every band reads as unlimited
755
+ * (AGL-3049). A dormant uncapped comp is `false` here and `comp.uncapped`.
756
+ */
757
+ uncapped: boolean;
758
+ decidedBy: OrgPlanDecidedBy;
759
+ }
760
+ /**
761
+ * How a staff chip names a stored comp (AGL-3049): the plan, then whether it
762
+ * is uncapped and whether a live subscription holds it dormant —
763
+ * `Enterprise (uncapped)`, `Pro (dormant)`, `Agency (uncapped, dormant)`.
764
+ *
765
+ * One reading of the terms, so the orgs list, the org summary and the
766
+ * override dialog cannot describe the same grant differently. `name` is the
767
+ * plan as the surface already writes plans: the label by default, the stored
768
+ * key on the surfaces whose other chips show keys.
769
+ */
770
+ export declare function planCompLabel(comp: Pick<ResolvedOrgPlanComp, 'plan' | 'uncapped'>, inForce: boolean, name?: string): string;
771
+ /**
772
+ * A stored comp as a sentence names it (AGL-3049): `uncapped Enterprise
773
+ * comp`, `Pro comp`. Shared by the plan description and the override route's
774
+ * account of what it changed.
775
+ */
776
+ export declare function planCompPhrase(comp: Pick<ResolvedOrgPlanComp, 'plan' | 'uncapped'>): string;
777
+ /**
778
+ * The org's plan, described rather than only resolved (AGL-3034) — the ONE
779
+ * reading the staff override route answers with and the staff override
780
+ * dialog renders, so the response and the dialog cannot tell staff two
781
+ * different stories about the same document.
782
+ */
783
+ export declare function describeOrgPlan(org: Partial<AglynOrgBilling> | null | undefined): OrgPlanDescription;
784
+ /**
785
+ * One sentence for staff saying what decides the plan and what a plan set in
786
+ * the override dialog would do (AGL-3034). Shared by the route's response and
787
+ * the dialog, for the reason `describeOrgPlan` is.
788
+ *
789
+ * A comp in force also says whether its bands are walls or lifted
790
+ * (AGL-3049), because that is the question a comped workspace refused at a
791
+ * band brings to staff.
792
+ */
793
+ export declare function orgPlanDescriptionSentence(description: OrgPlanDescription): string;
794
+ /**
795
+ * Whether the org is actually being charged for its plan right now.
796
+ *
797
+ * This is deliberately stricter than `resolveEffectivePlan`: entitlement
798
+ * asks "what does this org get", revenue asks "who is paying us". A staff
799
+ * plan override (`/admin/orgs`) never writes `subscription` — before AGL-3034
800
+ * it wrote `plan`, and now it writes a comp — so a comped or dark-launched
801
+ * org resolves to a paid plan while billing nothing, and counting it inflates
802
+ * MRR (AGL-925). No subscription mirror means no Stripe subscription means no
803
+ * revenue.
804
+ *
805
+ * A comp never reaches this answer: it reads the stored `plan` and the
806
+ * status, never `entitlements.planComp`, and a comp is only ever in force
807
+ * where the status is dead or absent — both of which answer false here.
808
+ *
809
+ * `past_due` counts: Stripe is still retrying and the plan is still owed.
810
+ */
811
+ export declare function isBillingSubscription(org: Partial<AglynOrgBilling> | null | undefined): boolean;
812
+ /**
813
+ * The org's monthly LIST price in USD — plan base plus every purchased add-on
814
+ * (manager/collaborator seats, datasets, extra hosts, POS registers, Event
815
+ * Calendar), BEFORE any applied discount, and 0 for anyone not actually
816
+ * billing. This is the sticker price the plan + add-ons would bill at.
817
+ *
818
+ * Annual subscriptions contribute their per-month equivalent
819
+ * (`basePriceAnnualMonthlyUsd`), not the month-to-month price, so a mixed
820
+ * book does not read high by the size of the annual discount. Add-ons bill
821
+ * per-month (annual variants are ×12 with no discount), so each contributes
822
+ * its monthly price on either interval.
823
+ *
824
+ * Pricing-v3 fix (2026-07): previously omitted the host, POS-register, and
825
+ * Event-Calendar add-ons, understating the total for every org carrying them.
826
+ *
827
+ * AGL-1105: split out of `orgMonthlyRevenueUsd`, which now subtracts an
828
+ * applied per-org discount on top of this. Callers wanting the pre-discount
829
+ * sticker (the margin guardrail, a "what would this bill at" quote) use this;
830
+ * callers wanting the actual money collected use `orgMonthlyRevenueUsd`.
831
+ */
832
+ export declare function orgListPriceMonthlyUsd(org: Partial<AglynOrgBilling> | null | undefined): number;
833
+ /**
834
+ * How many Aglyn AI add-ons a purchase map counts as: 0 or 1 (AGL-2896).
835
+ * The add-on is org-wide like Event Calendar, so a quantity above one — a
836
+ * hand edit, a doubled webhook item — is one purchase, and a non-number,
837
+ * `NaN` or a negative is none.
838
+ */
839
+ export declare function aiAddonUnits(addons: OrgSeatAddons | null | undefined): 0 | 1;
840
+ /**
841
+ * A list price net of a discount (AGL-1105). Percentage discounts scale the
842
+ * whole bill; fixed discounts subtract a flat USD amount. Never below 0, and
843
+ * a percentage is clamped to 0–100. Prefers `percentOff` when both are
844
+ * somehow set (Stripe coupons carry only one). Rounds to whole cents.
845
+ */
846
+ export declare function applyDiscountUsd(listPriceUsd: number, discount: Pick<OrgDiscount, 'percentOff' | 'amountOffUsd'> | null | undefined): number;
847
+ /**
848
+ * Whether an org should read as **"Enterprise"** everywhere the plan is shown.
849
+ * Three ways to qualify:
850
+ * - **`plan === 'enterprise'`** — the real plan (AGL-1118). This is the path
851
+ * every new enterprise org takes, and the only one that also grants
852
+ * Enterprise *entitlements*; the two below are display-only overlays on a
853
+ * lower base plan;
854
+ * - a billing org on a **negotiated custom price** (`customMonthlyUsd > 0`) —
855
+ * a paying enterprise deal still parked on its pre-AGL-1118 base plan; or
856
+ * - an explicit **`org.enterprise`** marker — a *comped* enterprise account,
857
+ * 100%-discounted so it collects $0 while infra cost is still metered.
858
+ *
859
+ * ⚠️ This function answers ONE question — "does this org read as Enterprise" —
860
+ * and it is not the same question as "what does this org get". Only the first
861
+ * bullet grants anything: `resolveEffectivePlan` reads `org.plan` and nothing
862
+ * else, so `org.enterprise` and `customMonthlyUsd` are read NOWHERE but here.
863
+ * A comped or custom-priced org resolves its BASE plan's entitlements, and
864
+ * gets Enterprise capability through a per-org `entitlements` override — see
865
+ * `ssoEnabled`, whose docblock calls that "how enterprise orgs provisioned
866
+ * before that plan existed still get it".
867
+ *
868
+ * This paragraph replaces a parenthetical claiming the comped marker carried
869
+ * "full Enterprise capability + SSO" (AGL-2297). It never did, and the billing
870
+ * page believed it: the Enterprise card ticked five entitlements against any
871
+ * org this function marked, so a comped org whose overrides granted SSO but
872
+ * not white-label was shown a green tick for full white-label while the
873
+ * branding card correctly refused it.
874
+ *
875
+ * The last two are kept for orgs provisioned before `enterprise` was a plan;
876
+ * migrating such an org to `plan: 'enterprise'` is the clean end state and
877
+ * changes nothing about how it reads here. List-priced orgs with none of the
878
+ * three are not enterprise.
879
+ *
880
+ * A staff comp in force names the plan instead of the stored one (AGL-3034),
881
+ * exactly as it does for entitlements: a workspace comped to Enterprise reads
882
+ * as Enterprise, and one comped to a lower tier does not read as Enterprise
883
+ * because of a plan stored before the comp. The explicit marker still counts.
884
+ */
885
+ export declare function isEnterpriseOrg(org: Partial<AglynOrgBilling> | null | undefined): boolean;
886
+ /**
887
+ * Human label shown for `enterprise` in plan badges. Kept as a named export
888
+ * for the surfaces that label a custom-priced org whose stored `plan` is still
889
+ * a lower tier; a real `plan: 'enterprise'` org gets the same string from
890
+ * `PLAN_LABELS`.
891
+ */
892
+ export declare const ENTERPRISE_PLAN_LABEL: string;
893
+ /**
894
+ * The org's monthly recurring revenue in USD — its LIST price
895
+ * (`orgListPriceMonthlyUsd`) net of any applied per-org discount
896
+ * (`org.discount`, AGL-1105), and 0 for anyone who is not actually billing. A
897
+ * comped enterprise deal genuinely collects less, so the discount belongs in
898
+ * MRR; the pre-discount sticker stays available as `orgListPriceMonthlyUsd`.
899
+ */
900
+ export declare function orgMonthlyRevenueUsd(org: Partial<AglynOrgBilling> | null | undefined): number;
901
+ /**
902
+ * Stripe's standard processing fee on Aglyn's OWN subscription charges
903
+ * (AGL-1108) — 2.9% + $0.30 per charge. This is the fee Aglyn pays Stripe to
904
+ * collect a subscription; it is DISTINCT from the storefront platform fee
905
+ * (`transactionFee*Pct`, the Connect `application_fee`) that a seller's own
906
+ * Stripe pays on their storefront sales. Revenue/margin should be reasoned on
907
+ * the NET figure, not gross.
908
+ */
909
+ export declare const STRIPE_PROCESSOR_FEE_PCT = 0.029;
910
+ export declare const STRIPE_PROCESSOR_FEE_FIXED_USD = 0.3;
911
+ /**
912
+ * A gross monthly USD amount net of Stripe's processing fee. Annual
913
+ * subscriptions are charged once per year, so the fixed 30¢ amortizes to
914
+ * $0.30/12 per month; monthly subscriptions pay it every month. Never
915
+ * returns below 0.
916
+ */
917
+ export declare function netOfProcessorFee(grossMonthlyUsd: number, annual?: boolean): number;
918
+ /**
919
+ * The org's monthly recurring revenue NET of Stripe's processing fee
920
+ * (AGL-1108) — what Aglyn actually keeps before infra COGS, and the correct
921
+ * base for margin/discount reasoning. Gross is `orgMonthlyRevenueUsd`; this
922
+ * subtracts ~2.9%+30¢, interval-aware. 0 for anyone not actually billing.
923
+ */
924
+ export declare function orgNetMonthlyRevenueUsd(org: Partial<AglynOrgBilling> | null | undefined): number;
925
+ /**
926
+ * Net-margin floor for discount underwriting (AGL-1105). Mirrors the
927
+ * Enterprise-Pricing-Calculator's ~75% gross-margin target: a discounted
928
+ * subscription should still keep ≥75% of its net (post-processor-fee) revenue
929
+ * after infrastructure COGS. Below the floor a discount is warned; well below
930
+ * it is blocked without explicit sign-off.
931
+ *
932
+ * WHAT THIS IS A FLOOR ON (AGL-1930): net revenue less INFRASTRUCTURE COGS,
933
+ * and nothing else. It is a contribution margin — see `MARGIN_SCOPE_NOTE`,
934
+ * which is the sentence every staff surface quoting this number has to print
935
+ * beside it.
936
+ *
937
+ * Read as a dollar guard it says: net revenue must be at least
938
+ * `cogs / (1 − floor)` = **4 × COGS**, and blocks below
939
+ * `cogs / (1 − floor + band)` ≈ **2.86 × COGS**. That restatement is the point
940
+ * of AGL-1930 — the percentage is scale-invariant, so the only thing that
941
+ * moves the real guard line in dollars is the COGS figure fed into it, and on
942
+ * production today that figure is `INFRA_COGS_PER_SITE_USD × sites` for
943
+ * **every** org (14 of 14 usage rollups across all 6 orgs, measured
944
+ * 2026-08-24). Re-tuning this constant therefore re-tunes a multiplier on the
945
+ * flat floor, not on anything measured. Calibrate the floor first.
946
+ */
947
+ export declare const NET_MARGIN_FLOOR_PCT = 0.75;
948
+ /**
949
+ * How far under the floor a discount may sit and still only WARN (AGL-1105).
950
+ * Within `[floor − band, floor)` staff see a caution; below `floor − band`
951
+ * the guardrail blocks unless overridden. 10 points, matching the
952
+ * calculator's soft/hard split.
953
+ */
954
+ export declare const NET_MARGIN_WARN_BAND_PCT = 0.1;
955
+ /**
956
+ * What every "net margin" figure in the staff console actually measures
957
+ * (AGL-1930).
958
+ *
959
+ * `checkDiscountMargin`, the enterprise quote card and the coupon badge all
960
+ * compute `(net revenue − infrastructure COGS) / net revenue`. Support,
961
+ * acquisition and overhead are not in the model anywhere — no meter records
962
+ * them and no constant estimates them — so "net margin 78%" is a CONTRIBUTION
963
+ * margin, and a staff member signing a deal on it is reading a ceiling, not a
964
+ * profit.
965
+ *
966
+ * One exported string rather than three literals: the three surfaces that
967
+ * print a margin already drifted apart once (AGL-1134), and a caveat that is
968
+ * true on one screen and absent on the next is worse than none.
969
+ */
970
+ export declare const MARGIN_SCOPE_NOTE: string;
971
+ /**
972
+ * The `ok` / `warn` / `block` band a cost-recovery margin falls in (AGL-1930).
973
+ *
974
+ * Extracted so `NET_MARGIN_FLOOR_PCT` and `NET_MARGIN_WARN_BAND_PCT` have ONE
975
+ * reader. The staff org page's enterprise quote card had `>= 0.75` and
976
+ * `>= 0.65` written into its JSX, so re-tuning either constant — the whole
977
+ * subject of AGL-1930 — would have moved the discount guardrail and left that
978
+ * card rating deals against the old numbers, silently and with money on the
979
+ * line.
980
+ */
981
+ export declare function netMarginRating(marginPct: number): DiscountMarginRating;
982
+ /**
983
+ * Per-site monthly infrastructure COGS in USD (AGL-1105). The
984
+ * Enterprise-Pricing-Calculator's ~$2/site figure — Firebase + Vercel at
985
+ * cost — charged against every host the org runs. The margin guardrail
986
+ * subtracts this from net revenue before rating a discount.
987
+ */
988
+ export declare const INFRA_COGS_PER_SITE_USD = 2;
989
+ /**
990
+ * Platform markup on passed-through infrastructure costs (AGL-41).
991
+ *
992
+ * DEFINED HERE, not in `apps/console/utils/usage-metering.ts`, and re-exported
993
+ * from there — the same move AGL-2155 made for `pageViewsFromBandwidthGb`, and
994
+ * for the same reason (AGL-2194). The published `/pricing` pass-through table
995
+ * states both a cost column and a "you pay (+30%)" column, and
996
+ * `tools/marketing/build-pricing-tables.mts` must generate both from code so
997
+ * `npm run check:pricing-tables` can fail on drift. That generator resolves no
998
+ * `@aglyn/*` path alias, so it can import `plan-entitlements.ts` and nothing
999
+ * under `apps/console`. A constant the published page depends on must live
1000
+ * where the page's generator can read it.
1001
+ *
1002
+ * The console keeps importing it from `usage-metering` unchanged.
1003
+ */
1004
+ export declare const METERED_MARKUP = 1.3;
1005
+ /**
1006
+ * Monthly unit costs for the meters the rollup already records (AGL-1134).
1007
+ *
1008
+ * These are OUR costs, not prices — the org is billed at cost × 1.30
1009
+ * (`METERED_MARKUP`, AGL-41). Storage/page-view/form rates match
1010
+ * `METERED_UNIT_RATES_USD` in the metering module on purpose: two files
1011
+ * disagreeing about what a page view costs is exactly the drift this function
1012
+ * exists to remove.
1013
+ *
1014
+ * The three added here are operator-tuned estimates of the same quality as
1015
+ * the existing three — Firestore-backed dataset storage is priced well above
1016
+ * object storage because it is, and API requests and contacts are priced off
1017
+ * the reads behind them. Treat every operator-tuned figure here as
1018
+ * provisional until validated against a real Firebase + Vercel invoice month;
1019
+ * AGL-1134 says so out loud rather than implying the numbers are derived.
1020
+ * `perEmailSend` is the exception and carries its own basis below.
1021
+ *
1022
+ * ## Three of these are shared with the billed table. The rest are NOT.
1023
+ *
1024
+ * `storagePerGbMonth`, `perPageView` and `perFormSubmission` are the metered
1025
+ * infrastructure pass-through, and the customer is charged those figures
1026
+ * times `METERED_MARKUP`. They therefore appear in `METERED_UNIT_RATES_USD`
1027
+ * as well, with identical values, and must be changed in both places at once.
1028
+ *
1029
+ * Everything else here is a cost-model input ONLY. `dataStoragePerGbMonth`,
1030
+ * `perApiRequest`, `perContactMonth` and `perEmailSend` price what an org
1031
+ * costs us; what an org is CHARGED on those axes is a retail rate on
1032
+ * `PLAN_PRICING` that descends with the tier and is not derived from cost.
1033
+ * Adding one of them to `METERED_UNIT_RATES_USD` would put it inside the
1034
+ * "at cost + 30%" claim, which is true of exactly three meters.
1035
+ *
1036
+ * The first three were corrected 2026-08-09 (AGL-1280) — storage $0.03 →
1037
+ * $0.026 and form submissions $0.0005 → $0.00005 — and `perPageView` was
1038
+ * re-pegged 2026-09-09 (AGL-2711), from $0.0001 to $0.00016153846. All three
1039
+ * MUST be changed here and in `METERED_UNIT_RATES_USD` together. See that
1040
+ * table for each figure's basis; it is the one a customer is billed against,
1041
+ * so it is the one that carries the working.
1042
+ *
1043
+ * The page-view basis is no longer an open gap. The rate priced a 627 KB page
1044
+ * against one that measured far more for weeks; the weight reduction that was
1045
+ * preferred over a re-peg has now shipped, the page was re-measured at
1046
+ * 976.1 KB, and the rate is pegged to a 1012.8 KB basis that sits ABOVE it.
1047
+ * `npm run check:page-view-rate` holds that peg, and now fails if the basis
1048
+ * ever drops back below what a published page measures.
1049
+ *
1050
+ * Raising the two here raises measured COGS, which changes no guardrail
1051
+ * verdict: `INFRA_COGS_PER_SITE_USD × sites` is the floor, and it still
1052
+ * outruns measured cost by orders of magnitude at real traffic.
1053
+ */
1054
+ export declare const ORG_COGS_UNIT_RATES_USD: {
1055
+ storagePerGbMonth: number;
1056
+ /**
1057
+ * ⛔ HALF OF A PAIR — see ESTIMATED_PAGE_TRANSFER_BYTES, which states the
1058
+ * same page in bytes as this states it in dollars. Both are calibrated
1059
+ * against a 1012.8 KB page and neither may move alone: their quotient is
1060
+ * the cost of a gigabyte, and that is what every bandwidthGb band on the
1061
+ * ladder was sized against.
1062
+ */
1063
+ perPageView: number;
1064
+ perFormSubmission: number;
1065
+ /** Firestore-backed dataset bytes — an order pricier than object storage. */
1066
+ dataStoragePerGbMonth: number;
1067
+ perApiRequest: number;
1068
+ perContactMonth: number;
1069
+ /**
1070
+ * One delivered email, whatever produced it — $0.90 per 1,000.
1071
+ *
1072
+ * Unlike its five neighbours this is not an operator-tuned estimate. It is
1073
+ * the provider's own blended rate at the tier above the account's current
1074
+ * one ($90 / 100,000) and also the marginal overage rate on both that tier
1075
+ * and the current one, so it does not fall as volume grows and is the
1076
+ * conservative figure at any usage the platform can reach.
1077
+ *
1078
+ * It prices EVERY send on the cost meter — campaigns, receipts, invites,
1079
+ * password resets — because every one of them is a message the provider
1080
+ * charges for. The band on the plan and the retail overage rate beside it
1081
+ * both concern a smaller quantity; this is the whole bill.
1082
+ */
1083
+ perEmailSend: number;
1084
+ /**
1085
+ * One workflow or action run — $12 per million.
1086
+ *
1087
+ * Derived from the run paths at list prices (2026-09-07), because nothing
1088
+ * measurable exists yet: runs execute inside the Vercel request that fired
1089
+ * the event, not in a function of their own, so Cloud Logging carries no
1090
+ * per-run entry to count, and Firestore's operation counts are platform-wide
1091
+ * and cannot be attributed to a run. Over the 14 days to 2026-09-07 the
1092
+ * project logged no run at all.
1093
+ *
1094
+ * The ledger, for an event that executes one workflow on a site holding a
1095
+ * handful of functions, variables and workflows (`runEventWorkflows`,
1096
+ * `runEventActions` and the inbound-webhook run are the same shape):
1097
+ *
1098
+ * - reads: the triggered query, the workflow map, the host → org lookup,
1099
+ * the run counter, the functions and the variables — about 12, at
1100
+ * nam5's $0.06 per 100,000 = $0.0000072
1101
+ * - writes: the activity row and the counter increment — 2, at $0.18 per
1102
+ * 100,000 = $0.0000036
1103
+ * - compute: a few milliseconds of active CPU and memory on Fluid compute
1104
+ * for the expression evaluation, about $0.000001
1105
+ *
1106
+ * $0.0000118, carried as $0.000012. It scales with the catalog a site holds
1107
+ * — every `limit(100)` read above is a query that costs one read per
1108
+ * document returned — and a step that writes a dataset record or calls a
1109
+ * webhook adds to it, so this is the typical figure, not the ceiling.
1110
+ *
1111
+ * It is a COST, not a price: no plan bills runs, and none refuses past the
1112
+ * band on a metered plan. It exists so a tier's run bands reach
1113
+ * `orgMonthlyCogsUsd` and `tier-margin-floor.spec.ts` — Agency's 3,000,000
1114
+ * runs are $36 a month that read as nothing while this was absent.
1115
+ */
1116
+ perRun: number;
1117
+ };
1118
+ /** The rollup fields `orgMonthlyCogsUsd` prices. All optional and all absent-safe. */
1119
+ export interface OrgUsageRollupInput {
1120
+ hostCount?: number | null;
1121
+ storageGb?: number | null;
1122
+ pageViews?: number | null;
1123
+ formSubmissions?: number | null;
1124
+ dataStorageMb?: number | null;
1125
+ apiRequests?: number | null;
1126
+ /**
1127
+ * Contacts alone — the figure every rollup carried before AGL-2611 widened
1128
+ * the band, kept so a month written under the old basis still prices.
1129
+ * Read only when `crmRecordsCount` is absent; see it.
1130
+ */
1131
+ contactsCount?: number | null;
1132
+ /**
1133
+ * Contacts + companies + deals, the CRM records band's own figure
1134
+ * (AGL-2611). Priced at `perContactMonth` — the rate was measured on a
1135
+ * contact, which is the most expensive of the three to hold, so it
1136
+ * over-covers a company or a deal rather than under-pricing one. When
1137
+ * present it REPLACES `contactsCount` in the model rather than adding to
1138
+ * it: the contacts are inside this sum, and a model that priced both
1139
+ * would charge the same people twice.
1140
+ */
1141
+ crmRecordsCount?: number | null;
1142
+ /** The components of `crmRecordsCount`, recorded so the sum is legible. */
1143
+ companiesCount?: number | null;
1144
+ dealsCount?: number | null;
1145
+ /**
1146
+ * Every email the org sent this month, campaigns and transactional alike —
1147
+ * the `emailSends` cost meter, not `campaignEmailSends`.
1148
+ *
1149
+ * The cost meter is the right input precisely because the campaign meter is
1150
+ * the one with a cap on it. What this org cost us is every message the
1151
+ * provider charged for, and most of them are messages no quota could have
1152
+ * refused.
1153
+ */
1154
+ emailSends?: number | null;
1155
+ /**
1156
+ * Aglyn Assist provider spend for the month, ALREADY IN DOLLARS (AGL-2280).
1157
+ *
1158
+ * Unlike every other field here this is not a meter to be priced — it is
1159
+ * `orgs/{id}/assistUsage/{month}.estCostUsd`, our own cost estimate at the
1160
+ * provider's list rates, computed where the tokens were counted. So it
1161
+ * enters the model at ×1 and has no entry in `ORG_COGS_UNIT_RATES_USD`;
1162
+ * inventing a second per-token rate here is precisely the drift AGL-1134
1163
+ * removed.
1164
+ */
1165
+ assistCostUsd?: number | null;
1166
+ /**
1167
+ * Workflow and action runs this month — the two counters `report-usage`
1168
+ * sums across the org's hosts. Priced together at `perRun`: a run is the
1169
+ * same handful of reads, two writes and a moment of compute whichever
1170
+ * builder produced it, and the bands differ only in which tier sells them.
1171
+ */
1172
+ workflowRuns?: number | null;
1173
+ actionRuns?: number | null;
1174
+ }
1175
+ export interface OrgCogsResult {
1176
+ /** What the guardrail should charge against revenue. */
1177
+ cogsUsd: number;
1178
+ /** Which arm produced it — `floor` means the meters came in under the flat estimate. */
1179
+ basis: 'measured' | 'floor';
1180
+ /** Cost from the meters alone, before the floor is applied. */
1181
+ measuredUsd: number;
1182
+ /** `INFRA_COGS_PER_SITE_USD` × sites. */
1183
+ floorUsd: number;
1184
+ /** Per-meter contributions, for showing the working in staff UI. */
1185
+ breakdown: Record<string, number>;
1186
+ }
1187
+ /**
1188
+ * One org's monthly infrastructure COGS — the single cost model (AGL-1134).
1189
+ *
1190
+ * Replaces two things that had drifted apart: the discount guardrail read the
1191
+ * rollup's `costUsd`, and the staff MRR views read the same field separately,
1192
+ * so neither could be changed without silently changing the other's meaning.
1193
+ *
1194
+ * `costUsd` was also incomplete. The rollup records `dataStorageMb`,
1195
+ * `apiRequests` and `contactsCount`, and `estimateMonthlyUsageCost` prices
1196
+ * none of them — it sums storage, page views and form submissions only. So
1197
+ * the "measured" cost omitted three meters the platform was already at the
1198
+ * trouble of recording.
1199
+ *
1200
+ * The flat per-site figure stays a FLOOR rather than a fallback: measured
1201
+ * cost wins only when it exceeds the estimate. A rollup that has not run, or
1202
+ * an org with genuinely tiny usage, must not make the guardrail more generous
1203
+ * — that is the one direction that costs money.
1204
+ *
1205
+ * Measured against production 2026-07-30, that floor is doing all the work:
1206
+ * the largest real org's `costUsd` was **$0.0000054** against a $4.00 floor
1207
+ * for two sites. Five orders of magnitude. So this function changes no verdict
1208
+ * today, and will not until there is real traffic — which is the honest
1209
+ * reason to build it now, while nothing depends on the answer.
1210
+ */
1211
+ export declare function orgMonthlyCogsUsd(rollup: OrgUsageRollupInput | null | undefined, siteCount: number): OrgCogsResult;
1212
+ /**
1213
+ * Pluck the fields `orgMonthlyCogsUsd` prices out of a raw usage rollup
1214
+ * (AGL-1134) — a Firestore `snapshot.data()`, or the JSON a staff endpoint
1215
+ * served from one. ONE list of field names, in one place.
1216
+ *
1217
+ * The hazard this closes is silent. Every caller previously hand-listed the
1218
+ * six fields at its own call site — the discount route, the anomaly detector,
1219
+ * the staff org page — and `/api/admin/org-usage` projected only three of
1220
+ * them. A projection that drops a priced field does not error; the model just
1221
+ * returns a smaller number, and a smaller COGS is the direction that APPROVES
1222
+ * a discount. Same shape as the API projection that starved a predicate in
1223
+ * AGL-1355, and the two-units usage figure that read 20-45% wrong for years
1224
+ * in AGL-1402.
1225
+ *
1226
+ * UNITS, because the field names do not all say: `storageGb` and
1227
+ * `dataStorageMb` are gigabytes and MEGABYTES respectively (the conversion
1228
+ * lives in `orgMonthlyCogsUsd`, with its own test); `pageViews`,
1229
+ * `formSubmissions`, `apiRequests` and `emailSends` are counts FOR THE MONTH;
1230
+ * `contactsCount` and `crmRecordsCount` are point-in-time levels, not monthly
1231
+ * flows.
1232
+ */
1233
+ export declare function orgCogsInputFrom(source: Record<string, unknown> | null | undefined): OrgUsageRollupInput;
1234
+ /**
1235
+ * A cost figure, or the explicit admission that the rollup has not loaded
1236
+ * (AGL-1134).
1237
+ *
1238
+ * `pending` is not "no usage" and not "$0" — it is "do not answer yet".
1239
+ */
1240
+ export type OrgCogsPreview = {
1241
+ status: 'pending';
1242
+ } | {
1243
+ status: 'ready';
1244
+ cogs: OrgCogsResult;
1245
+ };
1246
+ /**
1247
+ * `orgMonthlyCogsUsd` behind an explicit readiness flag (AGL-1134).
1248
+ *
1249
+ * The staff org page loads its rollup asynchronously, and an unfinished read
1250
+ * and an absent document are the same `undefined`. Priced directly, both
1251
+ * yield `basis: 'floor'` — so the enterprise pricing card printed "per-site
1252
+ * floor — no usage recorded yet" during the loading window, a
1253
+ * measurement-shaped sentence about an org whose measurements had simply not
1254
+ * arrived. That is the AGL-1380 / AGL-1422 defect exactly: a question
1255
+ * answered before its data is ready, and answered in the cheap direction.
1256
+ *
1257
+ * Returning a discriminated union rather than a nullable number is what makes
1258
+ * the window unrepresentable — a caller cannot reach a cost without first
1259
+ * handling `pending`, so the next call site cannot re-introduce this by
1260
+ * forgetting a flag it was handed.
1261
+ */
1262
+ export declare function orgCogsPreview(ready: boolean, rollup: OrgUsageRollupInput | null | undefined, siteCount: number): OrgCogsPreview;
1263
+ /**
1264
+ * Where a cost figure came from, in a sentence a staff member can act on
1265
+ * (AGL-1930).
1266
+ *
1267
+ * The staff quote card said `per-site floor — no usage recorded yet` for
1268
+ * EVERY `basis: 'floor'` org. Measured against production 2026-08-24 that
1269
+ * sentence is false on most of them: all 14 usage rollups across all 6 orgs
1270
+ * price under the floor, and 9 of the 14 carry genuinely non-zero usage — the
1271
+ * largest real org measured **$0.22** against its $2.00 floor. "No usage
1272
+ * recorded" and "usage recorded, and it came in under the floor" are opposite
1273
+ * facts about the cost model, and only the second one is evidence the meters
1274
+ * are working at all.
1275
+ *
1276
+ * Same failure family as AGL-1380 / AGL-1422 — a measurement-shaped sentence
1277
+ * printed over a measurement that was never consulted — one layer further in:
1278
+ * here the measurement exists and the copy denies it.
1279
+ */
1280
+ export declare function orgCogsBasisSummary(cogs: OrgCogsResult, month?: string | null): string;
1281
+ /**
1282
+ * Percent-off at or above which creating a coupon needs explicit staff
1283
+ * sign-off (AGL-1105). A ≥40%-off coupon is a real revenue commitment, so the
1284
+ * creation route rejects it unless the caller passes a confirm flag; the UI
1285
+ * surfaces a checkbox at this threshold.
1286
+ */
1287
+ export declare const DISCOUNT_APPROVAL_THRESHOLD_PCT = 40;
1288
+ /**
1289
+ * Discount DEPTH bands (AGL-1120) — how much of list price is being given
1290
+ * away, which is a different question from whether the remainder covers
1291
+ * infrastructure.
1292
+ *
1293
+ * The original guardrail asked only the second question, so a **93% off**
1294
+ * coupon rated "OK — net margin 78.1% vs a 75% floor": $9.15 left against $2
1295
+ * of infra really is a 78% contribution margin. The ratio is scale-invariant,
1296
+ * so as long as the leftover dwarfs $2 the verdict stays green no matter how
1297
+ * little is left. Depth never entered the verdict at all.
1298
+ *
1299
+ * Warn at the threshold that already requires sign-off, so the badge and the
1300
+ * approval checkbox stop disagreeing with each other; block at 70%, where a
1301
+ * deal is being given away rather than discounted.
1302
+ */
1303
+ export declare const DISCOUNT_DEPTH_WARN_PCT = 40;
1304
+ export declare const DISCOUNT_DEPTH_BLOCK_PCT = 70;
1305
+ /** How many sites (hosts) the org runs — at least 1 for a billing org. */
1306
+ export declare function orgSiteCount(org: Partial<AglynOrgBilling> | null | undefined): number;
1307
+ /** The margin-guardrail verdict for a proposed or applied discount. */
1308
+ export type DiscountMarginRating = 'ok' | 'warn' | 'block';
1309
+ export interface DiscountMarginResult {
1310
+ /** Pre-discount list price (`orgListPriceMonthlyUsd`). */
1311
+ grossUsd: number;
1312
+ /** List price net of the proposed discount. */
1313
+ discountedUsd: number;
1314
+ /** Discounted price net of Stripe's processing fee — what Aglyn keeps. */
1315
+ netUsd: number;
1316
+ /**
1317
+ * Monthly COGS charged against this org: the org's MEASURED cost when the
1318
+ * caller supplies one, else the flat `$/site × sites` estimate. The flat
1319
+ * figure is a floor, never a ceiling — a measured cost below it is treated
1320
+ * as the estimate, since a rollup that has not caught up must not make a
1321
+ * deal look cheaper than the placeholder did.
1322
+ */
1323
+ infraCogsUsd: number;
1324
+ /** True when `infraCogsUsd` came from measured usage, not the flat rate. */
1325
+ cogsMeasured: boolean;
1326
+ /**
1327
+ * Cost recovery = (net − COGS) / net, as a fraction. 1 = COGS-free; -1 is
1328
+ * the clamp for a fully-underwater discount (net ≤ 0), which always blocks.
1329
+ *
1330
+ * NOT a deal-quality number on its own — see `depthPct`. Kept under its
1331
+ * original name because the staff UI reports it verbatim.
1332
+ */
1333
+ marginPct: number;
1334
+ /** Share of list price given away, as a fraction (0.93 = 93% off). */
1335
+ depthPct: number;
1336
+ /** The worse of the depth and cost-recovery verdicts. */
1337
+ rating: DiscountMarginRating;
1338
+ /**
1339
+ * Which test produced `rating`, so the UI can say why rather than showing a
1340
+ * margin number that looks fine next to a red badge.
1341
+ */
1342
+ reason: 'none' | 'depth' | 'cogs' | 'underwater';
1343
+ /** The cost-recovery floor this was rated against (`NET_MARGIN_FLOOR_PCT`). */
1344
+ floorPct: number;
1345
+ }
1346
+ export interface DiscountMarginOptions {
1347
+ /**
1348
+ * The org's measured monthly cost, when the caller has it — the `costUsd`
1349
+ * rollup at `orgs/{id}/usage/{month}`. Server callers and the staff org
1350
+ * page can supply it; coupon creation cannot, because no org is chosen yet.
1351
+ */
1352
+ measuredCogsUsd?: number | null;
1353
+ }
1354
+ /**
1355
+ * Rate a proposed discount (AGL-1105, re-shaped in AGL-1120) — the guardrail
1356
+ * behind staff coupon creation and per-org application.
1357
+ *
1358
+ * Two independent questions, and the verdict is the WORSE of them:
1359
+ *
1360
+ * 1. **How deep is it?** `depthPct` against `DISCOUNT_DEPTH_WARN_PCT` /
1361
+ * `DISCOUNT_DEPTH_BLOCK_PCT`. This is the one that was missing. Cost
1362
+ * recovery alone is scale-invariant — 93% off $139 leaves $9.15 against
1363
+ * $2 of infra, a genuine 78% contribution margin — so the old verdict
1364
+ * rated a give-away "OK" and only warned at 94%.
1365
+ * 2. **Does the remainder cover the cost of serving them?** `marginPct`
1366
+ * against `NET_MARGIN_FLOOR_PCT`, with `NET_MARGIN_WARN_BAND_PCT` below
1367
+ * it, and an automatic block once the discount drives net ≤ 0.
1368
+ *
1369
+ * Neither subsumes the other: a shallow discount on an org running twenty
1370
+ * sites can still fail cost recovery, and a deep one on a cheap org can still
1371
+ * clear it. `reason` reports which test bound, so the UI never shows a
1372
+ * healthy-looking margin beside a red badge without explanation.
1373
+ *
1374
+ * `discount` is the PROPOSED change, independent of any discount already on
1375
+ * the org — `grossUsd` is always the list price (`orgListPriceMonthlyUsd`),
1376
+ * so this never double-counts an existing `org.discount`. A non-billing org
1377
+ * (list price 0) has no revenue to protect and rates `ok`.
1378
+ */
1379
+ export declare function checkDiscountMargin(org: Partial<AglynOrgBilling> | null | undefined, discount: Pick<OrgDiscount, 'percentOff' | 'amountOffUsd'>, options?: DiscountMarginOptions): DiscountMarginResult;
1380
+ /**
1381
+ * The plan the org actually gets (AGL-247): missing/unknown plans resolve
1382
+ * as `free`, and a paid plan whose subscription is canceled/unpaid/
1383
+ * incomplete downgrades to `free` until the webhook restores it — plan
1384
+ * fields alone are not entitlement.
1385
+ *
1386
+ * A STAFF COMP comes first (AGL-3034), and only where no subscription decides
1387
+ * the plan: `resolvePlanComp` answers `null` while one is live, so a paying
1388
+ * workspace's plan is always its subscription's. The comp is its own marker,
1389
+ * never the stored `plan` — a canceled customer whose `plan` still names what
1390
+ * they paid for resolves as Free exactly as before, because nothing about
1391
+ * their document is a comp.
1392
+ */
1393
+ export declare function resolveEffectivePlan(org: Partial<AglynOrgBilling> | null | undefined): OrgPlan;
1394
+ /**
1395
+ * Whether the org's Aglyn AI add-on currently applies (AGL-2896): bought,
1396
+ * and on a subscription that is still paying for it. The ONE reading of
1397
+ * `seatAddons.aiAddon` — the entitlement fold and the assist overage rate
1398
+ * both ask this, so a dead subscription drops the band and the rate
1399
+ * together rather than leaving a rate on a band that has gone.
1400
+ */
1401
+ export declare function hasAiAddon(org: Partial<AglynOrgBilling> | null | undefined): boolean;
1402
+ /**
1403
+ * Numeric entitlement keys that were REMOVED from `PLAN_ENTITLEMENTS` and
1404
+ * must never be resolved again from a stored override (AGL-2133).
1405
+ *
1406
+ * `totalSiteSizeMb` is the first. It carried a value on all 8 plans and was
1407
+ * enforced by nothing — no gate, no meter, no alert. AGL-1107 added it and
1408
+ * AGL-1370 deleted its meter and its alert on measurement rather than taste:
1409
+ * `measure-node-map.ts` refuses any node map over 900 KB (AGL-678), so the
1410
+ * measurable org total tops out at 2.3–20.9% of the cap depending on plan.
1411
+ * What survived was a WRITABLE staff override field for a number nothing
1412
+ * read, so a support engineer resolving a "this site is too big" ticket
1413
+ * raised it, got a success and an audit row, and changed nothing.
1414
+ *
1415
+ * WIRING IT WAS THE OTHER OPTION AND IT IS WORSE. A cap the measurement can
1416
+ * reach a fifth of is a gate that refuses nobody — the same "reports success,
1417
+ * does nothing" shape, moved from the staff dialog to the enforcement point,
1418
+ * plus a new hard wall introduced days before launch. The per-site size
1419
+ * ceiling that actually binds is AGL-678's 900 KB node map, and it is real.
1420
+ *
1421
+ * The rollup's `siteSizeMb` MEASUREMENT stays — host-usage and the usage
1422
+ * audit read it, and it is a genuine internal signal. Only the entitlement
1423
+ * goes.
1424
+ */
1425
+ export declare const RETIRED_ENTITLEMENT_KEYS: ReadonlySet<string>;
1426
+ /**
1427
+ * The numeric entitlements that are PRICES rather than caps (AGL-3049): the
1428
+ * platform's percentage of a storefront or marketplace sale.
1429
+ *
1430
+ * Every other numeric key is a band — a count, a size, a monthly or daily
1431
+ * allowance — and an uncapped comp lifts it to `UNLIMITED`. These three it
1432
+ * must never touch. `transactionFee*Pct` becomes Stripe's
1433
+ * `application_fee_amount` at checkout, so an unlimited one is a fee larger
1434
+ * than the charge and every sale on the storefront fails; and "no cap" has
1435
+ * no meaning for a price in the first place. An uncapped workspace pays its
1436
+ * plan's percentages, or whatever a per-org override sets them to.
1437
+ *
1438
+ * Named rather than derived from the `Pct` suffix, so a new numeric key is a
1439
+ * band unless someone decides here that it is a price. `plan-comp.spec.ts`
1440
+ * pins these three by name and proves every other numeric key is lifted.
1441
+ */
1442
+ export declare const PRICE_ENTITLEMENT_KEYS: ReadonlySet<string>;
1443
+ /**
1444
+ * Effective entitlements for an org: plan defaults with the org doc's
1445
+ * per-key overrides applied (features merge key-by-key too), then
1446
+ * purchased add-ons stacked on top (AGL-524): `seatAddons.hosts` raises
1447
+ * `hostLimit`, `seatAddons.eventCalendar` switches the `eventCalendar`
1448
+ * feature on, and every seat add-on a plugin declared through
1449
+ * `registerPluginEntitlements` (AGL-2940) widens the quota it names by its
1450
+ * per-plan figure per unit and switches its features on — the Aglyn AI
1451
+ * add-on (AGL-2896) is the first such declaration. (Seat/dataset add-ons
1452
+ * instead fold in at `checkSeatQuota` / `checkDatasetQuota`, where the
1453
+ * per-plan hard max clamps them.)
1454
+ * Missing or unknown plans resolve as `free`.
1455
+ *
1456
+ * EVERY NUMERIC KEY of `OrgEntitlements` is an override axis, not a chosen
1457
+ * few: the loop below copies any number it finds under the org's
1458
+ * `entitlements`, so a contracted `bandwidthGb`, `apiRequestsPerMonth` or
1459
+ * `workflowRunsPerMonth` resolves ahead of the plan default exactly as the
1460
+ * long-documented `emailSendsPerMonth`, `formsPerHost` and
1461
+ * `assistCreditsPerMonth` overrides do. Since 2026-09-07 this is what makes
1462
+ * Enterprise's finite fallbacks safe to hold: the row in `PLAN_ENTITLEMENTS`
1463
+ * is what an agreement gets before its numbers are written down, and the
1464
+ * override is where they are written. `plan-entitlements.spec.ts` proves
1465
+ * the precedence on every axis, so a resolver that started honoring a list
1466
+ * would fail by the name of the axis it dropped.
1467
+ *
1468
+ * `hostLimit` is DELIBERATELY unclamped here (AGL-1738): there is no
1469
+ * `maxHostsPerOrg` to clamp against, because extra sites are sold flat rather
1470
+ * than up to a band. Their bound is the purchase ceiling,
1471
+ * `EXTRA_HOSTS_ADDON_MAX`, and a band-shaped `Math.min` added here would
1472
+ * silently disable sites the org is invoiced for. See that constant before
1473
+ * "fixing" this add.
1474
+ *
1475
+ * `posRegisters` IS NOT ADDED HERE, and this is the fix, not an omission
1476
+ * (AGL-1775). The register add-on is priced "per
1477
+ * extra register/location" and enforced PER SITE, so folding the org-wide
1478
+ * purchase into an org-level value every site inherits sold one register and
1479
+ * delivered `hostLimit` of them. `seatAddons.posRegisters` is now a POOL and
1480
+ * `org.registerAllocations` says which site holds each seat. The number this
1481
+ * function returns for `posRegisters` is therefore **the plan's cap alone** —
1482
+ * exactly what an unallocated site must resolve to. Per-site capacity comes
1483
+ * from `resolveHostRegisterCap(org, hostId)`; nothing else may add the pool
1484
+ * back in.
1485
+ *
1486
+ * (`eventCalendar` is still org-wide on purpose: it is documented and priced
1487
+ * as "one purchase covers every host in the org". The register add-on never
1488
+ * was, which is the whole difference.)
1489
+ *
1490
+ * AN UNCAPPED COMP IN FORCE (AGL-3049) resolves every band as `UNLIMITED`
1491
+ * on top of all of the above — `withEveryCapLifted` — and leaves the
1492
+ * features and the fee percentages exactly as resolved.
1493
+ */
1494
+ export declare function resolveOrgEntitlements(org: Partial<AglynOrgBilling> | null | undefined): ResolvedOrgEntitlements;
1495
+ /**
1496
+ * How many units of a plugin-declared seat add-on a purchase map counts as
1497
+ * (AGL-2940): the stored quantity, capped at the declaration's `maxUnits`
1498
+ * — 1 for an org-wide add-on, where a doubled webhook item or a hand edit
1499
+ * above one is still one purchase. A non-number, `NaN`, a negative, or a
1500
+ * key nothing declared is none.
1501
+ */
1502
+ export declare function pluginSeatAddonUnits(addons: OrgSeatAddons | null | undefined, key: string): number;
1503
+ /**
1504
+ * Whether a plugin-declared seat add-on currently applies to the org: bought,
1505
+ * and on a subscription that is still paying for it — the one reading every
1506
+ * surface that shows or charges for the add-on goes through, so a dead
1507
+ * subscription drops the quota and the feature together.
1508
+ */
1509
+ export declare function hasPluginSeatAddon(org: Partial<AglynOrgBilling> | null | undefined, key: string): boolean;
1510
+ /**
1511
+ * The org's POS register pool (AGL-1775): how many seats were purchased, how
1512
+ * many are assigned to a site, and how many are left to assign.
1513
+ *
1514
+ * `purchased` runs through `resolvePurchasedAddons`, so a dead subscription
1515
+ * empties the pool exactly as it empties every other add-on — the seats stop
1516
+ * counting when they stop billing.
1517
+ *
1518
+ * `byHost` is the SANITISED allocation, and it never sums past `purchased`.
1519
+ * The write path validates before it stores, so an over-allocated map means
1520
+ * the data is stale or corrupt (a purchase reduced without the allocation
1521
+ * being trimmed, a partial write, a hand edit). Both directions of getting
1522
+ * that wrong cost money, so it is resolved rather than trusted: hosts are
1523
+ * walked in sorted-id order and granted what remains of the pool, which is
1524
+ * deterministic — the same host gets the same answer on every reader — and
1525
+ * cannot hand out capacity nobody paid for.
1526
+ *
1527
+ * Every value is coerced: a non-number, `NaN`, `Infinity` or a negative reads
1528
+ * as zero seats. An `Infinity` in this map would otherwise become an
1529
+ * unbounded per-site register cap, which is the one outcome the guard in
1530
+ * `resolveHostRegisterCap` exists to make impossible.
1531
+ */
1532
+ export declare function resolveRegisterSeatPool(org: Partial<AglynOrgBilling> | null | undefined): {
1533
+ purchased: number;
1534
+ allocated: number;
1535
+ available: number;
1536
+ byHost: Record<string, number>;
1537
+ };
1538
+ /**
1539
+ * How many POS registers one SITE may run (AGL-1775): the plan's per-site cap
1540
+ * plus whatever the org has assigned to that site out of the pool.
1541
+ *
1542
+ * THE GUARD, and the reason this is a function rather than a lookup. An
1543
+ * unallocated host — no entry in `registerAllocations`, a missing map, a
1544
+ * missing org, an org still loading — resolves to `resolveOrgEntitlements(org)
1545
+ * .posRegisters`, which since AGL-1775 is the PLAN's cap and nothing else.
1546
+ * Never the pooled total, and never `Infinity` unless the plan itself is
1547
+ * `UNLIMITED` (enterprise, where an unbounded cap is what was sold).
1548
+ *
1549
+ * `checkQuota(undefined)` resolving to the Free tier is the lesson this
1550
+ * inverts: an absent input there quietly answered a question in the
1551
+ * PERMISSIVE direction on the day it mattered. Here the absent input answers
1552
+ * in the direction of the plan, which is the conservative one — a site that
1553
+ * has been assigned seats and whose allocation failed to load runs fewer
1554
+ * registers than it paid for, which the console can say out loud, rather than
1555
+ * more than it paid for, which nothing would ever notice.
1556
+ *
1557
+ * There is deliberately no separate read: the allocation lives on the org doc
1558
+ * beside the entitlements, so a caller holding one holds the other.
1559
+ */
1560
+ export declare function resolveHostRegisterCap(org: Partial<AglynOrgBilling> | null | undefined, hostId: string | null | undefined): number;
1561
+ /**
1562
+ * The org's COLLABORATOR seat pool (AGL-2439): how many per-site collaborator
1563
+ * seats were purchased, how many are assigned to a site, and how many are
1564
+ * left to assign.
1565
+ *
1566
+ * The AGL-1775 register mechanism, applied to the key it was never applied to.
1567
+ * `seatAddons.members` is bought once, org-wide; `membersPerHost` is enforced
1568
+ * PER SITE. Adding one to the other handed the whole purchase to every site,
1569
+ * so an org with 20 sites that bought ONE extra collaborator seat received 20.
1570
+ * The quantity is now the size of an org-level pool and
1571
+ * `org.collaboratorAllocations` says which site holds each seat.
1572
+ *
1573
+ * Everything about the resolution is deliberately identical to
1574
+ * `resolveRegisterSeatPool` — the same clamp, the same sorted-id determinism,
1575
+ * the same coercion of a non-number / `NaN` / `Infinity` / negative to zero.
1576
+ * This is ONE mechanism used twice, not a second mechanism that happens to
1577
+ * rhyme: a reader who has understood the register pool has understood this,
1578
+ * and a fix to one is a fix to both.
1579
+ *
1580
+ * `purchased` runs through `resolvePurchasedAddons`, so a dead subscription
1581
+ * empties the pool exactly as it empties every other add-on.
1582
+ */
1583
+ export declare function resolveCollaboratorSeatPool(org: Partial<AglynOrgBilling> | null | undefined): {
1584
+ purchased: number;
1585
+ allocated: number;
1586
+ available: number;
1587
+ byHost: Record<string, number>;
1588
+ };
1589
+ /**
1590
+ * How many console COLLABORATORS one SITE may hold (AGL-2439): the plan's
1591
+ * per-site allowance plus whatever the org has assigned to that site out of
1592
+ * the pool, clamped to the plan's hard `maxMembersPerHost`.
1593
+ *
1594
+ * THE GUARD, and the reason this is a function rather than a lookup. An
1595
+ * unallocated host — no entry in `collaboratorAllocations`, a missing map, a
1596
+ * missing org, an org still loading — resolves to the PLAN's `membersPerHost`
1597
+ * and nothing else. Never the pooled total, and never `Infinity` unless the
1598
+ * plan itself is `UNLIMITED` (enterprise, where an unbounded cap is what was
1599
+ * sold). `checkQuota(undefined)` resolving to the Free tier is the lesson
1600
+ * this inverts: the absent input answers in the direction of the plan, which
1601
+ * is the conservative one.
1602
+ *
1603
+ * The clamp is what `checkSeatQuota` always did and is kept: collaborator
1604
+ * seats are sold up to a BAND (`maxMembersPerHost`), unlike registers, which
1605
+ * are sold flat. Assigning more pool seats to a site than the band allows
1606
+ * cannot lift it past the band — the org must upgrade the plan.
1607
+ *
1608
+ * There is deliberately no separate read: the allocation lives on the org doc
1609
+ * beside the entitlements, so a caller holding one holds the other.
1610
+ */
1611
+ export declare function resolveHostCollaboratorCap(org: Partial<AglynOrgBilling> | null | undefined, hostId: string | null | undefined): number;
1612
+ /**
1613
+ * Quota answer for a site's console COLLABORATORS (AGL-2439) — the
1614
+ * host-scoped `checkSeatQuota`, in the same `SeatQuotaResult` shape so the
1615
+ * existing call sites and refusal copy read identically.
1616
+ *
1617
+ * `checkSeatQuota(org, 'members', …)` is WRONG for a site now and this exists
1618
+ * so nobody has to remember why: since AGL-2439 that helper no longer folds
1619
+ * the org-wide purchase into a per-site number, so it answers the PLAN's cap
1620
+ * and would refuse a site the seats it holds.
1621
+ *
1622
+ * ## THE GRANDFATHER BOUNDARY — explicit, and this field is it
1623
+ *
1624
+ * The cap is corrected, and no org already above it is evicted or locked
1625
+ * out.
1626
+ *
1627
+ * The boundary is drawn between two different questions, and they are
1628
+ * different lines of code rather than one number used for two purposes:
1629
+ *
1630
+ * `allowed` — may this site take on ANOTHER collaborator? Bound
1631
+ * by `limit`, the corrected cap. This is the ONLY
1632
+ * thing the cap decides.
1633
+ * `retainedOverCap` — how many seats this site holds ABOVE `limit`. They
1634
+ * are RETAINED. Nothing in this repo revokes a
1635
+ * collaborator for being over a cap, and nothing may
1636
+ * be added that does: the cap binds ALLOCATION, never
1637
+ * ACCESS.
1638
+ *
1639
+ * So an over-cap site keeps every collaborator it has, cannot add another,
1640
+ * and returns to normal by attrition, by buying pool seats and assigning them
1641
+ * here, or by upgrading — at which point `limit` recomputes from the new plan
1642
+ * or allocation and the retention lapses on its own. That is what "they keep
1643
+ * what they have until they next change plan or seat count" means, resolved
1644
+ * rather than stored: a stored floor would need a backfill over live orgs and
1645
+ * would be a defaulted field on a `merge`-written document, which is the
1646
+ * converter hazard that destroys the real value it was meant to protect.
1647
+ *
1648
+ * `retainedOverCap` is non-zero ONLY when the site is already over. It is not
1649
+ * headroom and cannot be spent: it is a count the console says out loud so
1650
+ * the customer sees why the next Add is refused while nobody loses access.
1651
+ */
1652
+ export declare function checkHostCollaboratorQuota(org: Partial<AglynOrgBilling> | null | undefined, hostId: string | null | undefined, currentUsage: number): SeatQuotaResult & {
1653
+ /** Seats held ABOVE `limit`, retained and never revoked (the grandfather). */
1654
+ retainedOverCap: number;
1655
+ /** Pool seats the org has assigned to THIS site. */
1656
+ assignedSeats: number;
1657
+ };
1658
+ /**
1659
+ * Quota answer for a site's POS registers (AGL-1775) — the register-shaped
1660
+ * `checkQuota`, in the same `{ allowed, limit, remaining }` shape so callers
1661
+ * read identically.
1662
+ *
1663
+ * `checkQuota(org, 'posRegisters', used)` is WRONG for registers now and this
1664
+ * exists so nobody has to remember why: that helper resolves the org-level
1665
+ * value, which is the plan cap with no pool in it, so it would refuse a site
1666
+ * the seats it holds. Everything that gates a register creation calls this.
1667
+ */
1668
+ export declare function checkHostRegisterQuota(org: Partial<AglynOrgBilling> | null | undefined, hostId: string | null | undefined, currentUsage: number): {
1669
+ allowed: boolean;
1670
+ limit: number;
1671
+ remaining: number;
1672
+ };
1673
+ /**
1674
+ * Platform transaction fee % for a storefront sale (AGL-278): resolved
1675
+ * from the effective plan (with per-org overrides) by product type.
1676
+ * Digital and service sales use the digital rate; AGL-307 turns this into
1677
+ * the Stripe Connect `application_fee_amount` at charge time.
1678
+ */
1679
+ export declare function resolveTransactionFeePct(org: Partial<AglynOrgBilling> | null | undefined, productType: 'physical' | 'digital' | 'service'): number;
1680
+ /**
1681
+ * Stripe's processing cost on ONE storefront destination charge, in cents
1682
+ * (AGL-2152) — the dearest enabled method's percentage plus the fixed 30¢,
1683
+ * rounded UP on the percentage so the answer is never a cent short of what
1684
+ * Stripe actually debits.
1685
+ *
1686
+ * WHO PAYS THIS. Every storefront charge is a DESTINATION charge:
1687
+ * `payment_intent_data[transfer_data][destination]` names the merchant's
1688
+ * connected account and no `transfer_data[amount]` is sent, so Stripe moves
1689
+ * the whole charge to the merchant and debits its processing fee from the
1690
+ * PLATFORM's balance. Aglyn's net on an order is therefore
1691
+ *
1692
+ * application_fee_amount − (processing% × charge + 30¢)
1693
+ *
1694
+ * and with `application_fee_amount` at 0 — which is what
1695
+ * `transactionFeePhysicalPct: 0` produced on Pro, Business, Scale, Advanced,
1696
+ * Agency and Enterprise — that net is NEGATIVE on every single sale. A flat
1697
+ * percentage cannot repair it either: Starter's 2% is below even the cheapest
1698
+ * processing rate, so Starter lost money at every order size too, and any rate
1699
+ * above it still loses until the order clears `30¢ ÷ (take − processing)`. The
1700
+ * fixed 30¢ is why a percentage alone can never reach break-even on small
1701
+ * orders, and why this returns a CENTS amount rather than a rate.
1702
+ *
1703
+ * WHY THE DEAREST METHOD AND NOT 2.9%. The shopper picks how to pay AFTER the
1704
+ * session is created, and `application_fee_amount` is a fixed number Stripe
1705
+ * will not let us revise once the charge settles. No storefront path pins
1706
+ * `payment_method_types`, so the platform's payment method configuration
1707
+ * decides the set — and per AGL-2343 that configuration has `klarna` and
1708
+ * `affirm` on beside the card family. Pricing this at the card rate would be
1709
+ * wrong exactly on the orders where it cost the most, which is the same
1710
+ * reasoning `marketplaceBreakEvenUsd` already applies to the listing floor;
1711
+ * these are deliberately the SAME two constants so the two cannot drift.
1712
+ *
1713
+ * THE ONE-LINE LEVER. On a card-family order this over-recovers by the spread
1714
+ * between the two rates. Pinning `payment_method_types` on the storefront
1715
+ * sessions to the card family would make 2.9% the true binding rate, and
1716
+ * `STOREFRONT_PROCESSING_PERCENT` below is then the single identifier to
1717
+ * repoint. That is a product decision about which payment methods a storefront
1718
+ * offers, not this one; it is recorded on AGL-2152 and in the Pricing Decision
1719
+ * Log so it can be taken deliberately.
1720
+ */
1721
+ export declare function storefrontProcessingCostCents(chargeCents: number): number;
1722
+ /**
1723
+ * The Stripe Connect `application_fee_amount` for ONE storefront sale
1724
+ * (AGL-2152) — the platform's advertised take PLUS Stripe's processing cost,
1725
+ * passed through at cost.
1726
+ *
1727
+ * fee = take%(feeBaseCents) + processing%(chargeCents) + 30¢
1728
+ *
1729
+ * so Aglyn's net per order is AT LEAST the advertised take, at every order
1730
+ * size down to Stripe's own 50¢ charge minimum, and never negative. The
1731
+ * September-1 price lock was lifted for this one change; see the Pricing
1732
+ * Decision Log entry of 2026-08-19. No advertised PLATFORM rate moved — a 0% tier is still 0%
1733
+ * platform take — what changed is that the card cost stops being absorbed
1734
+ * silently by Aglyn.
1735
+ *
1736
+ * `feeBaseCents` is the GOODS the take is a cut of (discounts already applied,
1737
+ * tax and shipping excluded — that has always been the basis, AGL-2317).
1738
+ * `chargeCents` is what Stripe will actually run the card for, which is goods
1739
+ * + tax + shipping, and defaults to `feeBaseCents` for the paths where they
1740
+ * are the same number. Passing the goods figure for a charge that also carries
1741
+ * tax would under-recover by the processing rate on the difference.
1742
+ *
1743
+ * Clamped to `chargeCents` because Stripe rejects an application fee larger
1744
+ * than the charge. That clamp can only bind below ~32¢, under Stripe's own 50¢
1745
+ * charge minimum, so it never trims a real order's fee.
1746
+ */
1747
+ export declare function resolveTransactionFeeCents(org: Partial<AglynOrgBilling> | null | undefined, productType: 'physical' | 'digital' | 'service', feeBaseCents: number, chargeCents?: number): number;
1748
+ /**
1749
+ * Stripe's processing cost on a RECURRING charge of `amountCents`, as the
1750
+ * percent a Subscription's `application_fee_percent` can carry (AGL-2655).
1751
+ *
1752
+ * A Stripe Subscription accepts no `application_fee_amount` — only
1753
+ * `application_fee_percent`, a rate with at most two decimal places — so the
1754
+ * fixed 30¢ that `storefrontProcessingCostCents` adds in cents has to be
1755
+ * folded into the rate. `(rate × amount + fixed) ÷ amount` is, with the
1756
+ * amount cancelled,
1757
+ *
1758
+ * percent = rate + fixed ÷ amount
1759
+ *
1760
+ * which is why a smaller recurring price carries a HIGHER percent: the 30¢
1761
+ * is a bigger share of it. At 6% + 30¢ a $10 price carries 9%, $25 carries
1762
+ * 7.2% and $100 carries 6.3%.
1763
+ *
1764
+ * Computed in hundredths of a percent on integers and rounded UP to the next
1765
+ * hundredth. Up, for the reason the one-time helper rounds its percentage
1766
+ * up: rounding to nearest would leave the recovery a fraction of a cent
1767
+ * short of what Stripe debits half the time, and Stripe rejects a third
1768
+ * decimal outright, so the only honest direction is over. Integers, because
1769
+ * `0.09 * 100` is not `9` in floating point and a percent one ulp above a
1770
+ * whole hundredth would round up to the next one. The same two constants as
1771
+ * the one-time path, so repointing `STOREFRONT_PROCESSING_PERCENT` moves
1772
+ * both surfaces together. Zero for a charge that is not a charge.
1773
+ */
1774
+ export declare function storefrontProcessingPassThroughPercent(amountCents: number): number;
1775
+ /**
1776
+ * The `application_fee_percent` for ONE storefront subscription (AGL-2655):
1777
+ * the plan's advertised take PLUS Stripe's processing cost as a percent —
1778
+ * the recurring twin of `resolveTransactionFeeCents`, under the same rule.
1779
+ * Every tier carries the pass-through, a 0% tier included, because the
1780
+ * tier's 0% is a platform take and the pass-through is not one; it is what
1781
+ * Stripe charges to take the payment, which on a destination charge is
1782
+ * debited from the platform's balance whatever the tier.
1783
+ *
1784
+ * `amountCents` is the recurring items the percent will be applied to —
1785
+ * the goods after any discount, tax and shipping excluded, the AGL-2317
1786
+ * basis. Stripe applies the percent to the whole invoice and the paid-invoice
1787
+ * correction scales it back to items, so sizing on items is what makes the
1788
+ * recovery come out at cost rather than under.
1789
+ *
1790
+ * Composed in hundredths so two decimals stay two decimals (`2 + 6.34` is
1791
+ * not always `8.34` in floating point), and capped at 100, Stripe's ceiling
1792
+ * for the parameter — the recurring twin of the clamp to the charge.
1793
+ */
1794
+ export declare function resolveSubscriptionFeePercent(org: Partial<AglynOrgBilling> | null | undefined, productType: 'physical' | 'digital' | 'service', amountCents: number): number;
1795
+ /**
1796
+ * Platform take rate % for a MARKETPLACE listing sale (AGL-1543), priced
1797
+ * off the SELLER org. Resolved through `resolveOrgEntitlements` — so a
1798
+ * dead subscription prices as free-plan (30%) even while the stale `plan`
1799
+ * field still names a paid tier, and a negotiated per-org
1800
+ * `entitlements.marketplaceFeePct` override wins over the plan default.
1801
+ * Resolve per request from the org doc; never cache the answer.
1802
+ *
1803
+ * An out-of-range or missing value falls back to the FREE-plan rate — the
1804
+ * conservative direction. A malformed override must never zero the
1805
+ * platform's cut.
1806
+ */
1807
+ export declare function resolveMarketplaceFeePct(org: Partial<AglynOrgBilling> | null | undefined): number;
1808
+ /**
1809
+ * The marketplace take rate that breaks even LATEST, derived from the plan
1810
+ * table rather than copied from it (AGL-2343).
1811
+ *
1812
+ * The lowest rate is the binding one: a smaller cut of the same price against
1813
+ * the same processing cost needs a dearer listing to cover it, so advice quoted
1814
+ * at this band holds for every publisher on every plan. Derived on each call
1815
+ * because a hand-written second copy of a rate is exactly the artifact that
1816
+ * decays into wrong money copy shown to a publisher — the same reason
1817
+ * `planGrantingFeature` reads the table instead of a map.
1818
+ */
1819
+ export declare function bindingMarketplaceFeePct(): number;
1820
+ /**
1821
+ * What one marketplace sale costs the PLATFORM to process (AGL-2343).
1822
+ *
1823
+ * NONE OF THESE IS AN AGLYN PRICE. They are Stripe's published US rates and
1824
+ * the Texas rate the platform remits, and they exist only as inputs to a
1825
+ * figure shown to a publisher while they type. Nothing is billed from them —
1826
+ * the charged amounts are `resolveMarketplaceFeePct` and Stripe's own
1827
+ * invoicing — so changing one changes a sentence, never a bill.
1828
+ *
1829
+ * WHY THE PLATFORM PAYS THE PROCESSING FEE AT ALL. Marketplace checkout is a
1830
+ * DESTINATION charge with a fixed `transfer_data[amount]` and deliberately no
1831
+ * `application_fee_amount`, so that the sales tax stays with the platform that
1832
+ * owes it (AGL-1544). On a destination charge Stripe debits its fee from the
1833
+ * PLATFORM's balance, and it is charged on `amount_total` — the listing price
1834
+ * plus the tax added on top of it.
1835
+ *
1836
+ * So a $1 listing at a 20% take rate is a loss: the buyer pays $1.08, the
1837
+ * seller is transferred $0.80, $0.08 is owed to the state, Aglyn keeps $0.20
1838
+ * and pays Stripe about $0.33. Net −$0.13.
1839
+ *
1840
+ * THE BNPL RATE IS THE ONE THAT BINDS, and it is not hypothetical: the live
1841
+ * default payment method configuration has `klarna` and `affirm` enabled
1842
+ * alongside `card`, `cashapp`, `link`, `amazon_pay` and `apple_pay`, and the
1843
+ * marketplace session pins no `payment_method_types`, so a buyer may pay by
1844
+ * either. Break-even is computed from the dearest enabled method, because a
1845
+ * figure computed from the cheapest would be wrong exactly when it mattered.
1846
+ * IF BNPL IS EVER TURNED OFF FOR MARKETPLACE SESSIONS, this constant is what
1847
+ * has to move with it.
1848
+ */
1849
+ export declare const MARKETPLACE_PROCESSING_PERCENT_CARD = 2.9;
1850
+ /** Klarna/Affirm, roughly. See the note above — this is the binding one. */
1851
+ export declare const MARKETPLACE_PROCESSING_PERCENT_BNPL = 6;
1852
+ /** Stripe's per-transaction fixed component, in cents. */
1853
+ export declare const MARKETPLACE_PROCESSING_FIXED_CENTS = 30;
1854
+ /**
1855
+ * The tax rate the break-even figure assumes. Tax is added ON TOP of the
1856
+ * listing price and enlarges the base Stripe charges its percentage against,
1857
+ * so it makes the platform's cost slightly worse; the real rate is whatever
1858
+ * `automatic_tax` computes for the buyer's address, and this is the
1859
+ * platform's own Texas rate as a representative figure.
1860
+ */
1861
+ export declare const MARKETPLACE_ASSUMED_TAX_PERCENT = 8.25;
1862
+ /**
1863
+ * The processing rate a STOREFRONT destination charge is priced against
1864
+ * (AGL-2152), and the one identifier to repoint if the storefront's payment
1865
+ * method set changes.
1866
+ *
1867
+ * Deliberately its OWN name rather than a direct use of the marketplace
1868
+ * constants, while being defined as one of them: the two surfaces share
1869
+ * Stripe's published rates and today share the platform's payment method
1870
+ * configuration, but they are separate product decisions. A future choice to
1871
+ * pin `payment_method_types` on storefront sessions to the card family — and
1872
+ * so to recover at `MARKETPLACE_PROCESSING_PERCENT_CARD` — must not silently
1873
+ * lower the marketplace listing floor, which is derived from the same figure
1874
+ * for a different flow.
1875
+ */
1876
+ export declare const STOREFRONT_PROCESSING_PERCENT = 6;
1877
+ /** Stripe's per-transaction fixed component, in cents. Not rate-dependent. */
1878
+ export declare const STOREFRONT_PROCESSING_FIXED_CENTS = 30;
1879
+ /** Where the money goes on one sale of a paid listing (AGL-2343). */
1880
+ export interface MarketplaceSaleEconomics {
1881
+ /** The listing price. */
1882
+ priceCents: number;
1883
+ /** Added on top, and owed to the state. */
1884
+ taxCents: number;
1885
+ /** What the buyer is charged. */
1886
+ buyerPaysCents: number;
1887
+ /** `transfer_data[amount]` — the seller's share of the pre-tax price. */
1888
+ sellerReceivesCents: number;
1889
+ /** The platform's take. */
1890
+ platformFeeCents: number;
1891
+ /** Stripe's fee, debited from the platform on a destination charge. */
1892
+ processingCents: number;
1893
+ /** `platformFeeCents - processingCents`. Negative is a loss on the sale. */
1894
+ platformNetCents: number;
1895
+ }
1896
+ /**
1897
+ * The funds flow for one sale, mirroring what `checkout.ts` actually builds
1898
+ * (AGL-2343): tax exclusive and on top, a fixed transfer of the seller's share
1899
+ * of the PRE-tax price, and Stripe's fee charged on the buyer's total.
1900
+ */
1901
+ export declare function marketplaceSaleEconomics(priceUsd: number, feePercent: number, processingPercent?: number): MarketplaceSaleEconomics;
1902
+ /**
1903
+ * The cheapest WHOLE-DOLLAR price at which the platform does not lose money on
1904
+ * a sale (AGL-2343).
1905
+ *
1906
+ * Whole dollars because every publish route rounds `priceUsd` to one, so a
1907
+ * fractional break-even is not a price anyone can actually set. Searched
1908
+ * rather than solved algebraically so that it can never disagree with
1909
+ * `marketplaceSaleEconomics` — the rounding in there is what decides the
1910
+ * answer at these amounts, and a closed form would drift from it silently.
1911
+ *
1912
+ * THIS IS THE ENFORCED FLOOR: `marketplaceMinPriceUsd` returns this figure
1913
+ * and every publish door refuses a paid listing under it. Recorded in the Pricing
1914
+ * Decision Log and on AGL-2343.
1915
+ */
1916
+ export declare function marketplaceBreakEvenUsd(feePercent?: number, processingPercent?: number): number;
1917
+ /**
1918
+ * THE MINIMUM PRICE a paid marketplace listing may carry (AGL-2343).
1919
+ *
1920
+ * The floor is not a chosen round number — it is the break-even price itself,
1921
+ * the
1922
+ * cheapest whole dollar at which `marketplaceSaleEconomics` stops returning a
1923
+ * negative platform net, computed at the take rate that breaks even LATEST and
1924
+ * the dearest payment method the live configuration enables. Today that is $3.
1925
+ *
1926
+ * DERIVED, NEVER RESTATED. A second hand-written copy of this number in a
1927
+ * publish route or a form is the artifact that decays into a route refusing a
1928
+ * price a form invited, so both the server-side validator
1929
+ * (`MARKETPLACE_MIN_PRICE_USD` in the marketplace model) and every price field
1930
+ * read this. If the payment method set changes, or the plan table's lowest
1931
+ * take rate moves, the floor moves with them on the next call.
1932
+ *
1933
+ * ZERO IS NOT BELOW THE FLOOR. A free listing takes no payment at all, so it
1934
+ * costs nothing to process; the floor is a minimum on PAID listings only.
1935
+ */
1936
+ export declare function marketplaceMinPriceUsd(): number;
1937
+ /**
1938
+ * Whether a price is a PAID price that the floor refuses (AGL-2343) — the one
1939
+ * predicate a form uses to mark its price field in error and hold its publish
1940
+ * button, so that the button and the server agree about what is publishable.
1941
+ *
1942
+ * Rounds first because every publish route does, and a form's value is a
1943
+ * string mid-type: `'2.6'` is stored as $3 and must read as allowed here, or
1944
+ * the button would refuse a price the route accepts.
1945
+ */
1946
+ export declare function isBelowMarketplacePriceFloor(priceUsd: unknown): boolean;
1947
+ /**
1948
+ * The sentence a publish form shows under its price field when the price is
1949
+ * below the floor (AGL-2343), or `undefined` when there is nothing to say.
1950
+ *
1951
+ * NOT ADVISORY ANY MORE. The same figure is enforced by every publish route
1952
+ * through `publishPreconditionRefusal`, so this text has to read as a
1953
+ * requirement rather than a warning — a publisher who is told "consider
1954
+ * raising it" and then refused has been lied to by the form. It still explains
1955
+ * WHY, because a bare minimum with no arithmetic behind it reads as an
1956
+ * arbitrary tax on cheap listings.
1957
+ *
1958
+ * Shared by every publish form rather than written into each, because the
1959
+ * figure has to be the same one in all of them and the wording is the only
1960
+ * place a publisher ever sees it.
1961
+ *
1962
+ * Quoted at the take rate that breaks even LATEST, so the sentence holds for a
1963
+ * free-plan publisher too.
1964
+ */
1965
+ export declare function marketplacePriceCostNote(priceUsd: unknown): string | undefined;
1966
+ /**
1967
+ * The standing helper sentence a price field shows when there is nothing wrong
1968
+ * (AGL-2343): the minimum stated up front, so a publisher meets it while
1969
+ * typing instead of discovering it as a refusal.
1970
+ *
1971
+ * A capability that is not surfaced in the console does not count as shipped,
1972
+ * and that applies to a REFUSAL as much as to a feature: a floor only a route
1973
+ * knows about is a trap.
1974
+ *
1975
+ * @param suffix whatever else that particular form needs to say, appended.
1976
+ */
1977
+ export declare function marketplacePriceFloorHint(suffix?: string): string;
1978
+ /**
1979
+ * The cheapest plan whose BASE features include `feature`, in ladder order —
1980
+ * the answer to "which plan do I need for this?", which is the one thing an
1981
+ * upsell has to say and the one thing no gate can hard-code without drifting
1982
+ * from the table it is quoting.
1983
+ *
1984
+ * Returns `undefined` when NO plan carries it on the base tier. That is not
1985
+ * an error: `eventCalendar` is an add-on purchased through
1986
+ * `resolveOrgEntitlements`' override path and is false on all eight tiers, so
1987
+ * "upgrade to X" is the wrong sentence for it. Callers must handle undefined
1988
+ * rather than defaulting to a tier name.
1989
+ *
1990
+ * Derived from PLAN_ENTITLEMENTS on every call rather than from a
1991
+ * hand-written map: a table of "feature → plan" is exactly the artifact that
1992
+ * decays silently when a tier's flags are re-cut, and it would decay into
1993
+ * pricing copy shown to a customer.
1994
+ *
1995
+ * Ladder order is `SELF_SERVE_PLANS` then `enterprise` — the same order the
1996
+ * plan grid renders — so an enterprise-only feature (`ssoEnabled`) reports
1997
+ * Enterprise rather than nothing.
1998
+ */
1999
+ export declare function planGrantingFeature(feature: keyof OrgFeatureFlags): OrgPlan | undefined;
2000
+ /**
2001
+ * The plan name an upsell should name, ready to interpolate — "Business",
2002
+ * or undefined when {@link planGrantingFeature} has no answer.
2003
+ */
2004
+ export declare function planLabelGrantingFeature(feature: keyof OrgFeatureFlags): string | undefined;
2005
+ /**
2006
+ * True when the org's plan (or overrides) enables the boolean feature. A
2007
+ * key outside `OrgFeatureFlags` is one a plugin declared (AGL-2940) and is
2008
+ * answered the same way.
2009
+ */
2010
+ export declare function checkEntitlement(org: Partial<AglynOrgBilling> | null | undefined, feature: keyof OrgFeatureFlags | (string & {})): boolean;
2011
+ /**
2012
+ * The branding profile shape and the platform's own fallback, re-exported
2013
+ * for every caller that reads them alongside the entitlement gate.
2014
+ *
2015
+ * They are DEFINED in `platform-brand.ts`, beside the environment-driven
2016
+ * literals they are assembled from. A published tenant page renders the
2017
+ * attribution badge from `PLATFORM_BRANDING_PROFILE` and needs nothing else
2018
+ * in this module; importing it from here would put the whole plan, quota and
2019
+ * entitlement table into every anonymous visitor's first load.
2020
+ */
2021
+ export type { ResolvedBrandingProfile } from './platform-brand';
2022
+ export { PLATFORM_BRANDING_PROFILE } from './platform-brand';
2023
+ /**
2024
+ * The effective brand for an org (White-Label Phase 1). EVERY branded
2025
+ * surface — published-site branding, transactional email, console chrome —
2026
+ * MUST read the brand through this one resolver so a white-label org can
2027
+ * never partly-render as Aglyn (the multi-surface drift that dogged
2028
+ * `removeBranding`, which each surface re-derived on its own).
2029
+ *
2030
+ * When the org carries the `whiteLabel` entitlement (Agency tier, or an
2031
+ * Enterprise per-org override), the org's `brandingProfile` values win and
2032
+ * any field it leaves blank falls back to the Aglyn default. Without the
2033
+ * entitlement the stored profile is ignored entirely and the full Aglyn
2034
+ * brand is returned — so an org that white-labeled and then downgraded
2035
+ * reverts cleanly.
2036
+ */
2037
+ export declare function resolveBrandingProfile(org: Partial<AglynOrgBilling> | null | undefined): ResolvedBrandingProfile;
2038
+ /**
2039
+ * The brand as MERGE TOKENS, for a staff-designed system-email template
2040
+ * (AGL-2139).
2041
+ *
2042
+ * White-label inverted precisely when staff published a template, which is
2043
+ * that feature's normal steady state. Every org-context sender has the shape
2044
+ * `subject: designed?.subject ?? <branded fallback>` — the DESIGNED template
2045
+ * wins — and the catalog copy hard-coded "Aglyn" throughout. There was no
2046
+ * brand token to design against either: the merge maps carried `org.name`,
2047
+ * `invite.role`, `signInUrl` and friends and nothing about the brand, and
2048
+ * `blankUnresolvedTokens` BLANKS any token the caller did not supply. So a
2049
+ * designer who reached for `{{brand.productName}}` would have shipped an
2050
+ * email with a hole in the sentence.
2051
+ *
2052
+ * Token bodies are namespaced `brand.*` so a designer can tell them from the
2053
+ * per-send values, and they resolve for EVERY org — Aglyn's own included,
2054
+ * since `PLATFORM_BRANDING_PROFILE.productName` is `'Aglyn'`. That is what makes
2055
+ * a single template correct for both populations instead of one hard-coded
2056
+ * for the majority.
2057
+ *
2058
+ * The email LOGO is deliberately absent. It is structural — `renderEmailHtml`
2059
+ * emits it as a header row above the designed body, or emits nothing — so it
2060
+ * travels as an option rather than as a token a template could forget to
2061
+ * place, and there is no sample URL a staff test-send could render without
2062
+ * showing a broken image.
2063
+ */
2064
+ export declare function brandMergeTokens(branding: ResolvedBrandingProfile): Record<string, string>;
2065
+ /**
2066
+ * The value of the `<meta name="generator">` tag and of the `x-powered-by`
2067
+ * header on published sites (AGL-2088) — the canonical CMS signal WordPress,
2068
+ * Squarespace, Drupal and Ghost all ship, and the thing tech-stack detectors
2069
+ * key on.
2070
+ *
2071
+ * Deliberately VERSIONLESS. The accidental headers this replaced carried
2072
+ * `1.0.0-alpha.0` and a Node version, which dated every deployment and named
2073
+ * our runtime to no consumer. A detector needs the product name and nothing
2074
+ * else.
2075
+ */
2076
+ export declare const PLATFORM_GENERATOR_NAME: string;
2077
+ /**
2078
+ * May this org's published pages say they were built with Aglyn? (AGL-2088)
2079
+ *
2080
+ * The gate for the `<meta name="generator">` tag and the `x-powered-by`
2081
+ * header. It reads the SAME entitlement `resolveBrandingProfile` reads —
2082
+ * `whiteLabel` — rather than inventing a parallel one, so an agency's
2083
+ * concealment promise has exactly one definition.
2084
+ *
2085
+ * WHY `whiteLabel` AND NOT `showBranding`. The "Made with Aglyn" badge is
2086
+ * gated on `removeBranding`, which every plan above free grants, so reusing
2087
+ * that gate would confine the signal to free-tier sites — the corpus this
2088
+ * exists to build would be almost empty on day one. The two promises are also
2089
+ * different in kind: `removeBranding` buys a page with no Aglyn credit on it,
2090
+ * an aesthetic commitment about what a VISITOR sees; `whiteLabel` buys
2091
+ * concealment of who built the site, which is the promise a machine-readable
2092
+ * fingerprint actually breaks. A paying customer on a mid plan has bought a
2093
+ * clean page, not anonymity.
2094
+ *
2095
+ * ⚠️ AN UNRESOLVED ORG SUPPRESSES, and this is the whole reason the function
2096
+ * exists instead of a bare `!checkEntitlement(org, 'whiteLabel')` at each call
2097
+ * site. `resolveOrgEntitlements(null)` resolves to the FREE plan — a loading
2098
+ * default answering a question it was never asked — so a null org reads as
2099
+ * "no white-label" and would EMIT. That null is not hypothetical: the tenant's
2100
+ * `getOrgBilling` fails open with `org: null` on a Firestore error, so a
2101
+ * transient read failure on an Agency site would have stamped the generator
2102
+ * tag onto it. The asymmetry decides the direction: suppressing wrongly costs
2103
+ * us one detection sample, emitting wrongly breaks a paid promise on a
2104
+ * customer's own domain. So the org must be PRESENT and lack the entitlement;
2105
+ * absent is not the same as free here.
2106
+ */
2107
+ export declare function showsPlatformAttribution(org: Partial<AglynOrgBilling> | null | undefined): boolean;
2108
+ /**
2109
+ * Whether the org has actually **configured** a white-label brand (AGL-1110) —
2110
+ * a non-empty product name or logo. White-label chrome should activate only
2111
+ * when this is true, not merely because the plan grants the `whiteLabel`
2112
+ * entitlement: an Agency org (or a comped Enterprise org) that never set up a
2113
+ * brand keeps the plain Aglyn wordmark — no product-name-text render, no
2114
+ * flash — until it opts in on the branding page.
2115
+ */
2116
+ export declare function hasBrandingProfile(org: Partial<AglynOrgBilling> | null | undefined): boolean;
2117
+ /**
2118
+ * Quota check: `allowed` is false once `currentUsage` meets the limit —
2119
+ * call before creating the next resource (e.g. usage=hostCount before
2120
+ * creating another host). `remaining` never goes negative.
2121
+ */
2122
+ /**
2123
+ * `members` = per-site collaborator seats (legacy key, AGL-888): the key
2124
+ * matches persisted `seatAddons.members` and must not be renamed.
2125
+ */
2126
+ export type SeatKind = 'managers' | 'members';
2127
+ export interface SeatQuotaResult {
2128
+ /** False once usage meets the effective limit (included + purchased). */
2129
+ allowed: boolean;
2130
+ /** Effective seat limit: included + purchased addons, clamped to the max. */
2131
+ limit: number;
2132
+ remaining: number;
2133
+ /** Included seats on the plan before addons. */
2134
+ included: number;
2135
+ /** Purchased addon seats currently applied. */
2136
+ purchased: number;
2137
+ /** Hard cap incl. addons; reaching it requires a plan upgrade. */
2138
+ maxSeats: number;
2139
+ /**
2140
+ * True when buying more addon seats cannot raise the limit — either the
2141
+ * plan sells no addons or the hard cap is reached. UI should prompt an
2142
+ * upgrade instead of an addon purchase.
2143
+ */
2144
+ upgradeRequired: boolean;
2145
+ /** Monthly price per addon seat; null when the plan sells none. */
2146
+ addonPriceUsd: number | null;
2147
+ }
2148
+ /**
2149
+ * Seat quota check (AGL-112): seats differ from plain quotas because orgs
2150
+ * can buy addon seats (`org.seatAddons`) up to a per-plan hard max —
2151
+ * beyond the max the only path is upgrading the plan. `managers` counts
2152
+ * org-manager seats org-wide; `members` counts per-site COLLABORATORS
2153
+ * (`hosts/{id}/members`, viewer/editor/admin — legacy key name, AGL-888).
2154
+ * End-user member accounts (`siteMembers`) are unlimited and never pass
2155
+ * through here.
2156
+ *
2157
+ * SINCE AGL-2439 THIS IS THE PLAN'S CAP FOR `members`, with no purchased
2158
+ * seats in it — see the comment at the add below. A per-site answer must come
2159
+ * from `checkHostCollaboratorQuota(org, hostId, used)`, which is the only
2160
+ * thing that knows which site holds which pool seat. Every caller that has a
2161
+ * `hostId` must use that one; this stays correct for a plan-level readout
2162
+ * (what the plan includes per site, before anything is assigned).
2163
+ */
2164
+ export declare function checkSeatQuota(org: Partial<AglynOrgBilling> | null | undefined, kind: SeatKind, currentUsage: number): SeatQuotaResult;
2165
+ export declare function checkQuota(org: Partial<AglynOrgBilling> | null | undefined, quota: keyof Omit<ResolvedOrgEntitlements, 'features'>, currentUsage: number): {
2166
+ allowed: boolean;
2167
+ limit: number;
2168
+ remaining: number;
2169
+ };
2170
+ export interface DatasetQuotaResult {
2171
+ /** False once usage meets the effective limit (included + purchased). */
2172
+ allowed: boolean;
2173
+ /** Effective dataset limit: included + purchased, clamped to the max. */
2174
+ limit: number;
2175
+ remaining: number;
2176
+ included: number;
2177
+ purchased: number;
2178
+ maxDatasets: number;
2179
+ /** True when addons cannot raise the limit — upgrade instead. */
2180
+ upgradeRequired: boolean;
2181
+ /** Monthly price per extra dataset; null when the plan sells none. */
2182
+ addonPriceUsd: number | null;
2183
+ }
2184
+ /**
2185
+ * Dataset quota check (AGL-132/240), mirroring `checkSeatQuota`: orgs can
2186
+ * buy addon datasets (`org.seatAddons.datasets`, org-wide) up to the
2187
+ * plan's hard max; beyond the max the only path is upgrading.
2188
+ */
2189
+ export declare function checkDatasetQuota(org: Partial<AglynOrgBilling> | null | undefined, currentUsage: number): DatasetQuotaResult;
2190
+ export interface DataStorageQuotaResult {
2191
+ /**
2192
+ * False only when the plan hard-blocks (no overage pricing) and usage
2193
+ * meets the included size; metered plans always allow and bill overage.
2194
+ */
2195
+ allowed: boolean;
2196
+ /** Included dataset storage on the plan, MB. */
2197
+ includedMb: number;
2198
+ usedMb: number;
2199
+ /** Remaining included storage, MB; 0 once into overage. */
2200
+ remainingMb: number;
2201
+ /** Usage beyond the included size, GB (0 when within the plan). */
2202
+ overageGb: number;
2203
+ /** Estimated overage this month at the plan's per-GB rate. */
2204
+ overageMonthlyUsd: number;
2205
+ /** Per-GB-month overage rate; null when the plan meters nothing. */
2206
+ overageRateUsd: number | null;
2207
+ }
2208
+ /**
2209
+ * Org dataset-storage meter (AGL-240): aggregate stored document bytes
2210
+ * across `orgs/{orgId}/datasets`. Plans with an `extraDataGbMonthlyUsd`
2211
+ * rate meter the overage onto the monthly invoice (cost-plus, AGL-41);
2212
+ * plans without one (free) hard-block at the included size.
2213
+ */
2214
+ export declare function checkDataStorageQuota(org: Partial<AglynOrgBilling> | null | undefined, usedMb: number): DataStorageQuotaResult;
2215
+ export interface ApiRequestQuotaResult {
2216
+ /** Metered plans always allow; plans without API access block. */
2217
+ allowed: boolean;
2218
+ /** Included requests this month at the plan. */
2219
+ included: number;
2220
+ used: number;
2221
+ /** Remaining included requests; 0 once into overage. */
2222
+ remaining: number;
2223
+ /** Requests beyond the included quota (0 within the plan). */
2224
+ overageRequests: number;
2225
+ /** Estimated overage this month at the plan's per-1,000 rate. */
2226
+ overageMonthlyUsd: number;
2227
+ /** Per-1,000 overage rate; null when the plan has no API. */
2228
+ overageRateUsd: number | null;
2229
+ }
2230
+ /**
2231
+ * Customer REST API request meter (AGL-634): the monthly request count for an
2232
+ * org. Business and every tier above it carry `apiAccess` and an
2233
+ * `extraApiRequestsUsdPer1k` rate, so requests past the included quota meter
2234
+ * onto the monthly invoice
2235
+ * (cost-plus, like storage overage — never a hard wall mid-integration); plans
2236
+ * without API access have `included: 0` and always block.
2237
+ */
2238
+ export declare function checkApiRequestQuota(org: Partial<AglynOrgBilling> | null | undefined, usedRequests: number): ApiRequestQuotaResult;
2239
+ export interface CrmRecordsQuotaResult {
2240
+ /** Metered plans always allow; free hard-bands at the included count. */
2241
+ allowed: boolean;
2242
+ /** Included CRM records on the plan — the `contactsPerHost` band. */
2243
+ included: number;
2244
+ /** Contacts + companies + deals, as the caller counted them. */
2245
+ used: number;
2246
+ /** Remaining included records; 0 once into overage. */
2247
+ remaining: number;
2248
+ /** Records beyond the included band (0 within the plan). */
2249
+ overageRecords: number;
2250
+ /** Estimated overage this month at the plan's per-1,000 rate. */
2251
+ overageMonthlyUsd: number;
2252
+ /** Per-1,000 overage rate; null when the plan hard-bands (free). */
2253
+ overageRateUsd: number | null;
2254
+ }
2255
+ /**
2256
+ * The name this result carried while the band counted contacts alone.
2257
+ * @deprecated Read `CrmRecordsQuotaResult`; the shape is the same.
2258
+ */
2259
+ export type ContactQuotaResult = CrmRecordsQuotaResult;
2260
+ /**
2261
+ * CRM records band meter (AGL-890, widened in AGL-2611): contacts, companies
2262
+ * and deals, counted together across the org and measured against the ONE
2263
+ * band `contactsPerHost` names. Paid plans carry an `extraContactsUsdPer1k`
2264
+ * rate, so records past the included band meter onto the monthly invoice
2265
+ * (cost-plus, like storage and API overage — a growing audience is never
2266
+ * dropped); free has no rate and hard-bands at the included count, refusing
2267
+ * the next record of any of the three kinds.
2268
+ *
2269
+ * ONE band for three collections, and not three bands, because a company or
2270
+ * a deal costs less to hold than a contact — a ~2 KB document with few index
2271
+ * entries and no dynamic-list scan — so counting them at the contacts rate
2272
+ * over-covers them, and a second and third number to explain would buy
2273
+ * nothing a customer could act on. The caller passes the SUM; the server
2274
+ * helper that takes the three aggregate reads is `countCrmRecords`, and the
2275
+ * billing meter shows the three components in its caption so the sum is
2276
+ * legible rather than opaque. Tasks and activities are not counted: they
2277
+ * are bounded by human effort, and the activity log is capped per record by
2278
+ * `CRM_ACTIVITIES_PER_RECORD_CEILING` instead.
2279
+ *
2280
+ * End-user member ACCOUNTS are unlimited on every plan (AGL-889) — this
2281
+ * meters only the CRM record.
2282
+ */
2283
+ export declare function checkCrmRecordsQuota(org: Partial<AglynOrgBilling> | null | undefined, usedRecords: number): CrmRecordsQuotaResult;
2284
+ /**
2285
+ * The name the band's gate carried while it counted contacts alone. The SAME
2286
+ * function — every caller that still says "contact" is measuring the records
2287
+ * band and must hand it the summed count, not the contacts aggregate.
2288
+ * @deprecated Call `checkCrmRecordsQuota`.
2289
+ */
2290
+ export declare const checkContactQuota: typeof checkCrmRecordsQuota;
2291
+ /**
2292
+ * `orgs/{orgId}/crmEmailUsage/{YYYY-MM-DD}` — the durable per-day counter of
2293
+ * one-to-one CRM emails, `{ count, day, updatedAt }`, server-written only and
2294
+ * member-readable, exactly as `apiUsage/{month}` is for API requests.
2295
+ */
2296
+ export declare const CRM_EMAIL_USAGE_COLLECTION = "crmEmailUsage";
2297
+ /**
2298
+ * The document id of today's one-to-one email counter, `YYYY-MM-DD` in UTC.
2299
+ *
2300
+ * UTC rather than the workspace's zone, for the reason `apiUsageMonth` keys
2301
+ * the API counter in UTC: the cap is enforced on a server that has no zone
2302
+ * of the customer's to read, and a boundary that moved with a browser's
2303
+ * clock would let one day's sends be counted against two.
2304
+ */
2305
+ export declare function crmEmailUsageDayKey(now?: Date): string;
2306
+ export interface CrmEmailQuotaResult {
2307
+ /** Whether ONE MORE send is inside today's cap. Enterprise always allows. */
2308
+ allowed: boolean;
2309
+ /** The plan's daily cap; 0 where the tier has no CRM. */
2310
+ included: number;
2311
+ /** Sends already counted today. */
2312
+ used: number;
2313
+ /** Sends left today; 0 at the cap. */
2314
+ remaining: number;
2315
+ /** When the counter rolls over — the next UTC midnight. */
2316
+ resetsAt: Date;
2317
+ }
2318
+ /**
2319
+ * One-to-one CRM email cap (AGL-2611): how many messages a workspace may
2320
+ * still send from CRM records today, mirroring `checkApiRequestQuota`.
2321
+ *
2322
+ * A HARD cap with no overage rate, on every tier — the one respect in which
2323
+ * it differs from the API meter it is shaped after. `allowed` is therefore
2324
+ * `used < included` outright. Every paid tier's cap is finite, Enterprise's
2325
+ * 2,000 included, and 0 on Free makes it false at zero, so a per-org
2326
+ * `features.crm` grant does not send until `crmEmailsPerDay` is raised
2327
+ * beside it.
2328
+ *
2329
+ * ## The contract for the send route
2330
+ *
2331
+ * The route that sends a one-to-one email takes the send as a SLOT on
2332
+ * `orgs/{orgId}/crmEmailUsage/{crmEmailUsageDayKey()}` inside one Firestore
2333
+ * transaction — `reserveCrmEmailSend` in `@aglyn/tenant-data-admin` reads
2334
+ * `count`, calls this with that figure, REFUSES with the plan reason when
2335
+ * `allowed` is false and otherwise writes `used + 1` in the same transaction
2336
+ * (AGL-2645). A read-then-write cap let two reps at the last slot both pass
2337
+ * one reading of it; the transaction re-runs the second on the raised
2338
+ * figure. The slot is taken before the message leaves and handed back by
2339
+ * `releaseCrmEmailSend` when the provider refuses it. A delivered send is
2340
+ * then recorded on the org's `emailSends` cost meter through
2341
+ * `recordEmailSends` as `transactional`, because every message the provider
2342
+ * charged for is priced into COGS and the campaign cap does not govern it.
2343
+ * The count enforced is the count billed, from one document, the way the
2344
+ * API meter works.
2345
+ *
2346
+ * Read the counter with `used` from the server: the console meter on the
2347
+ * billing page reads the same document, member-readable, so a rep is never
2348
+ * told their limit by being refused.
2349
+ */
2350
+ export declare function checkCrmEmailQuota(org: Partial<AglynOrgBilling> | null | undefined, usedToday: number, now?: Date): CrmEmailQuotaResult;
2351
+ /**
2352
+ * Volume above the plan's included band, in emails.
2353
+ *
2354
+ * Transactional mail is never refused, so an org CAN and will finish a month
2355
+ * over its included allowance. That is not an error and not a failed send —
2356
+ * it is the overage the cap chose not to enforce, and recording it is what
2357
+ * keeps it from being a surprise at invoicing. `UNLIMITED` (and any
2358
+ * non-positive limit, which is how "no included allowance" is expressed)
2359
+ * yields 0 rather than the whole month's volume.
2360
+ *
2361
+ * DEFINED HERE and re-exported from `email-metering.ts` (AGL-2155's move):
2362
+ * the billing page renders this figure before anything is invoiced, and it
2363
+ * is a client component that cannot pull in the Admin SDK. One subtraction,
2364
+ * so the readout and the invoice cannot disagree.
2365
+ */
2366
+ export declare function emailSendsOverage(total: number, limit: number): number;
2367
+ export interface EmailSendOveragePrice {
2368
+ /** Emails past the plan's included band, as handed in. */
2369
+ overageSends: number;
2370
+ /** Estimated charge this month at the plan's per-1,000 rate. */
2371
+ overageMonthlyUsd: number;
2372
+ /** Per-1,000 rate; null when the plan carries no email overage price. */
2373
+ overageRateUsd: number | null;
2374
+ }
2375
+ /**
2376
+ * Prices email volume past a plan's included band.
2377
+ *
2378
+ * ## No `allowed`, deliberately
2379
+ *
2380
+ * Every other meter's helper here answers "may this proceed" alongside "what
2381
+ * does it cost", because on those axes one gate decides both. Email has two
2382
+ * gates that are not the same gate: `reserveCampaignEmailSends` refuses a
2383
+ * CAMPAIGN at the band inside a transaction, and nothing at all refuses
2384
+ * transactional mail at any tier. A field named `allowed` on this result
2385
+ * would be a plausible-looking thing for a sender to consult, and a
2386
+ * transactional sender consulting it is how a password reset stops going out.
2387
+ * So the result carries money and nothing else.
2388
+ *
2389
+ * ## Takes the overage, does not recompute it
2390
+ *
2391
+ * `emailSendsOverage` already derives the excess from the cost meter and the
2392
+ * resolved band, on the path that records it. Re-deriving it here from a raw
2393
+ * total would be a second overage model to drift from the first — which is
2394
+ * the shape AGL-1402 documented, where two figures that should have been one
2395
+ * disagreed by 20-45% for years.
2396
+ *
2397
+ * A null rate yields zero, structurally rather than by a check, which is what
2398
+ * keeps free at zero on this axis for the same reason it is zero on the other
2399
+ * three.
2400
+ */
2401
+ export declare function priceEmailSendOverage(org: Partial<AglynOrgBilling> | null | undefined, overageSends: number): EmailSendOveragePrice;
2402
+ /**
2403
+ * Is this org's plan subject to the metered infrastructure pass-through
2404
+ * (AGL-1280)? The single answer behind three otherwise-unrelated behaviours:
2405
+ * whether storage/bandwidth/form usage past the included band is BILLED, and
2406
+ * whether the form-submission cap is a wall or a meter.
2407
+ *
2408
+ * Deliberately a plan-level question, not an org-level one — an org with no
2409
+ * plan resolves as free, so an unknown org meters nothing. Billing usage to
2410
+ * someone with no subscription is the one error direction with no recovery.
2411
+ * A staff comp is that someone too (AGL-3034): it grants a paid plan with no
2412
+ * subscription behind it, so `resolvePlanPricing` answers `false` for it and
2413
+ * its bands are walls.
2414
+ */
2415
+ export declare function planMetersInfraOverage(org: Partial<AglynOrgBilling> | null | undefined): boolean;
2416
+ export interface FormSubmissionQuotaResult {
2417
+ /**
2418
+ * False only when the plan hard-caps (no metered pass-through) and usage
2419
+ * has reached the included count; metered plans always accept and bill
2420
+ * the excess.
2421
+ */
2422
+ allowed: boolean;
2423
+ /** Included submissions this month for this site. */
2424
+ included: number;
2425
+ used: number;
2426
+ /** Remaining included submissions; 0 once into overage. */
2427
+ remaining: number;
2428
+ /** Submissions beyond the included count (0 within the plan). */
2429
+ overageSubmissions: number;
2430
+ /** True when overage meters instead of blocking. */
2431
+ metered: boolean;
2432
+ }
2433
+ /**
2434
+ * Monthly form-submission meter (AGL-76, made coherent in AGL-1280).
2435
+ *
2436
+ * Submissions were BOTH hard-capped at `formSubmissionsPerMonth` (429) and
2437
+ * metered — which made the published promise that usage past the
2438
+ * included band is billed rather than cut off false for the one meter where
2439
+ * being cut off costs the customer a lead. Now it mirrors the other three
2440
+ * overage meters exactly: a plan with the metered pass-through always accepts
2441
+ * and prices the excess (`estimateMonthlyUsageCost`); free has no
2442
+ * subscription to bill, so its included count stays a hard wall.
2443
+ *
2444
+ * Priced per ORG at cost-plus, but capped per SITE, because that is where the
2445
+ * counter lives (`hosts/{id}/counters/formSubmissions`). Callers gating a
2446
+ * single submission pass that site's month count.
2447
+ */
2448
+ export declare function checkFormSubmissionQuota(org: Partial<AglynOrgBilling> | null | undefined, usedThisMonth: number): FormSubmissionQuotaResult;
2449
+ /**
2450
+ * How far past a plan's own included band the abuse ceiling sits (AGL-1655).
2451
+ * Ten times what the customer bought is not growth — a site that legitimately
2452
+ * outgrows its band by an order of magnitude has an upgrade conversation
2453
+ * waiting, not a silent invoice.
2454
+ */
2455
+ export declare const FORM_ABUSE_CEILING_MULTIPLE = 10;
2456
+ /**
2457
+ * Absolute floor for the abuse ceiling, so the small plans get real headroom
2458
+ * rather than a second, tighter plan limit wearing a different name. Starter
2459
+ * includes 200/month; 10× would be 2,000, which a genuinely busy site could
2460
+ * reach. 5,000 is ~167 submissions a day on ONE site — no legitimate site on
2461
+ * a small plan reaches that, and no small customer can be billed past
2462
+ * 5,000 × $0.000065 ≈ $0.33 of abuse.
2463
+ */
2464
+ export declare const FORM_ABUSE_CEILING_FLOOR = 5000;
2465
+ /**
2466
+ * Ceiling for plans whose included count is UNLIMITED (Agency, Enterprise),
2467
+ * where a multiple has nothing to multiply. Kept at or above the ceiling of
2468
+ * every finite plan below it (Advanced includes 100,000 → 1,000,000) so the
2469
+ * ladder never inverts and a bigger plan never gets the smaller ceiling.
2470
+ */
2471
+ export declare const FORM_ABUSE_CEILING_UNLIMITED = 1000000;
2472
+ export interface FormAbuseCeilingResult {
2473
+ /** True once this month's count for the SITE has reached the ceiling. */
2474
+ exceeded: boolean;
2475
+ /** The ceiling this site's plan resolves to. Always finite. */
2476
+ ceiling: number;
2477
+ used: number;
2478
+ }
2479
+ /**
2480
+ * Per-site monthly abuse ceiling for form submissions (AGL-1655).
2481
+ *
2482
+ * Deliberately NOT part of `checkFormSubmissionQuota`. That function answers
2483
+ * a plan question — "did the customer buy this?" — and AGL-1280 correctly
2484
+ * made its answer on a metered plan always yes, because usage past the band
2485
+ * bills rather than blocks. This one answers a different question: "is this
2486
+ * still a customer's traffic at all?" Conflating them is how the plan gate
2487
+ * ended up as the anti-abuse control it was never designed to be, and how
2488
+ * `allowed: metered ? true : …` came to mean an unbounded bill.
2489
+ *
2490
+ * So the ceiling is containment, not capacity. It is far above anything a
2491
+ * legitimate site reaches, it is the same shape on every plan, and crossing
2492
+ * it is an INCIDENT: the caller is expected to refuse the write, leave the
2493
+ * billable counter alone, and make the trip visible to the site's managers
2494
+ * and to staff. A ceiling that silently absorbed submissions would only move
2495
+ * the free plan's lost-lead failure up to a bigger number.
2496
+ *
2497
+ * Evaluated against the same month counter the plan gate reads — which is
2498
+ * Admin-SDK-only (`counters` is excluded from every client write in
2499
+ * `cloud/firebase-firestore.rules`), so the count behind the ceiling cannot
2500
+ * be lowered to launder past it.
2501
+ */
2502
+ export declare function checkFormSubmissionAbuseCeiling(org: Partial<AglynOrgBilling> | null | undefined, usedThisMonth: number): FormAbuseCeilingResult;
2503
+ /**
2504
+ * Average transfer per page view (HTML + JS + a few images), the constant
2505
+ * that turns the analytics view counter into a bandwidth number and back.
2506
+ *
2507
+ * ## ⛔ HALF OF A PAIR. It moves with `perPageView`, or neither moves.
2508
+ *
2509
+ * This is the BYTES one page view weighs;
2510
+ * {@link ORG_COGS_UNIT_RATES_USD}`.perPageView` is the DOLLARS the same page
2511
+ * view costs. They are one physical measurement written in two units, and
2512
+ * what every `bandwidthGb` band is actually sized against is neither of them
2513
+ * alone but their quotient — `(1 GB ÷ this) × perPageView`, the cost of a
2514
+ * gigabyte. That quotient is stable even while the page's weight is not,
2515
+ * which is precisely why moving one half re-prices every band on the ladder
2516
+ * without touching a band: a heavier page costs more per view and buys fewer
2517
+ * views per gigabyte, and the two cancel.
2518
+ *
2519
+ * Both are therefore stated against the SAME weight — **1012.8 KB**, the
2520
+ * basis recorded in `tools/tenant-page-budget.json` and recoverable from the
2521
+ * rate itself — and `npm run check:page-view-rate` compares the two and
2522
+ * refuses a mismatch. There is no version of this constant that is correct
2523
+ * against a `perPageView` calibrated for a different page.
2524
+ *
2525
+ * The basis is FIRST-PARTY encoded bytes at settle, because that is what an
2526
+ * egress bill is denominated in: third-party tags leave someone else's
2527
+ * network and cost the platform nothing, so counting them here would inflate
2528
+ * a customer's measured bandwidth with bytes nobody pays us for.
2529
+ *
2530
+ * Lives HERE rather than beside the metering rates (AGL-2155). It used to sit
2531
+ * in `apps/console/utils/usage-metering.ts`, which the console imports and the
2532
+ * tenant app cannot — and the bandwidth ceiling below has to be evaluated in
2533
+ * the tenant app, at the beacon that writes the counter. Re-deriving the
2534
+ * conversion there would have been a fourth hand-rolled copy of
2535
+ * `× 1024³ / page weight`, which is the exact drift AGL-1371 collapsed into
2536
+ * one function. `usage-metering` now re-exports these three, so every existing
2537
+ * importer is unchanged and there is still one definition.
2538
+ */
2539
+ export declare const ESTIMATED_PAGE_TRANSFER_BYTES: number;
2540
+ /**
2541
+ * Bandwidth ⇄ page views: the one conversion three surfaces used to each
2542
+ * write out by hand (AGL-1371).
2543
+ *
2544
+ * `bandwidthGb` is not a second cap next to metered page views — it IS the
2545
+ * included band of the page-view meter, expressed in the unit customers
2546
+ * understand. It is therefore used in both directions: forward by
2547
+ * `meteredIncludedAllowance` to size the band the invoice subtracts, backward
2548
+ * by the usage-alerts cron and the console meter to render live page views as
2549
+ * GB.
2550
+ */
2551
+ export declare function pageViewsFromBandwidthGb(bandwidthGb: number): number;
2552
+ /** @see pageViewsFromBandwidthGb — the same constant, the other way. */
2553
+ export declare function bandwidthGbFromPageViews(pageViews: number): number;
2554
+ /**
2555
+ * How far past a plan's own included bandwidth the abuse ceiling sits
2556
+ * (AGL-2155). Three times the bandwidth the customer bought is not growth; it
2557
+ * is an upgrade conversation, or it is not the customer's traffic at all.
2558
+ *
2559
+ * It is a CAP, not a price, and it is lower than {@link
2560
+ * FORM_ABUSE_CEILING_MULTIPLE} because bandwidth is the largest cost line the
2561
+ * platform carries and the one a stranger can spend on the customer's behalf.
2562
+ * Past the band every 1,000 views bills $0.21 against about $0.16 of real
2563
+ * cost, so the tail is no longer a loss — but a scraper, a hotlinked asset or
2564
+ * a botnet still bills the account holder for traffic they did not ask for,
2565
+ * and the ceiling is what bounds that before staff look at it. At 10× the
2566
+ * band that tail was open-ended on Agency; at 3× it is bounded.
2567
+ */
2568
+ export declare const BANDWIDTH_ABUSE_CEILING_MULTIPLE = 3;
2569
+ /**
2570
+ * Absolute floor for the bandwidth ceiling, in PAGE VIEWS per month, so the
2571
+ * small plans get real headroom instead of a second, tighter plan limit
2572
+ * wearing a different name.
2573
+ *
2574
+ * Free includes 2 GB ≈ 2,071 views; 3× would be ~6,212, which a genuinely
2575
+ * successful hobby site (a post that lands on Hacker News) reaches in an
2576
+ * afternoon and would be a miserable first experience of the platform.
2577
+ * 100,000 views/month ≈ 96.6 GB ≈ **$16 of real COGS** at
2578
+ * `METERED_UNIT_RATES_USD.perPageView` — well above the free band, and an
2579
+ * order of magnitude below the $162 a million views costs. It is the number
2580
+ * that makes "free stays free" true without making it stingy.
2581
+ */
2582
+ export declare const BANDWIDTH_ABUSE_CEILING_FLOOR = 100000;
2583
+ /**
2584
+ * Ceiling for an org whose bandwidth band resolves to UNLIMITED — a
2585
+ * contracted per-org override, since no plan row carries the sentinel any
2586
+ * more — where a multiple has nothing to multiply. Kept at or above the
2587
+ * ceiling of every finite band below it so the ladder never inverts and a
2588
+ * bigger plan never inherits a smaller ceiling.
2589
+ */
2590
+ export declare const BANDWIDTH_ABUSE_CEILING_UNLIMITED = 500000000;
2591
+ /**
2592
+ * Machine-readable marker for a bandwidth containment, so a capped response
2593
+ * is distinguishable from the maintenance/lockdown notices it shares a shell
2594
+ * with. Deliberately NOT a {@link LockdownReasonCode}: a lockdown is a staff
2595
+ * takedown, this is a billing containment that clears itself at the month
2596
+ * boundary with nobody doing anything.
2597
+ */
2598
+ export declare const BANDWIDTH_CEILING_CODE = "bandwidth_ceiling";
2599
+ export interface BandwidthAbuseCeilingResult {
2600
+ /** True once this month's page views for the SITE reached the ceiling. */
2601
+ exceeded: boolean;
2602
+ /** The ceiling this site's plan resolves to, in page views. Finite. */
2603
+ ceiling: number;
2604
+ used: number;
2605
+ }
2606
+ /**
2607
+ * Per-site monthly bandwidth abuse ceiling (AGL-2155).
2608
+ *
2609
+ * ## The hole this closes
2610
+ *
2611
+ * Every other free dimension had a runtime brace — `mediaStorageGate`,
2612
+ * `checkFormSubmissionQuota`, a zero band for dataset storage and API, a
2613
+ * hard band for contacts. Bandwidth had **none**: nothing anywhere refused a
2614
+ * page view, so a free site that went viral served a million views — roughly
2615
+ * **$162 of real COGS** — with no wall, no throttle and no alert. It was
2616
+ * protected only by the structural zero on the billing side (free carries no
2617
+ * rate, so the invoice stays $0), which protects the *bill* and not the
2618
+ * *bleeding*. `free-tier-never-billed.spec.ts` said so in its own table.
2619
+ *
2620
+ * ## Why an abuse ceiling and not a plan gate
2621
+ *
2622
+ * Exactly the distinction {@link checkFormSubmissionAbuseCeiling} draws, and
2623
+ * for the same reason. A plan gate answers "did the customer buy this?" — and
2624
+ * for a metered plan the answer past the band is *yes, and we bill it*. This
2625
+ * answers "is this still a customer's traffic at all?", and crossing it is an
2626
+ * INCIDENT rather than a quota: the site is flagged, staff are told, and the
2627
+ * render path degrades to a static notice instead of paying ~40 Firestore
2628
+ * reads and ~1,013 KB of egress per view.
2629
+ *
2630
+ * ## Where it is evaluated
2631
+ *
2632
+ * NOT in the render path. The page-view counter is written *after* the render
2633
+ * by `/api/analytics/collect`; that route evaluates this on a sampled cadence
2634
+ * and stamps `bandwidthCeiling` onto the host document. The render path reads
2635
+ * that field off a host doc **it already loads** (`load-page-data.ts` reads
2636
+ * `hostRes.host` for the lockdown branch), so the containment costs the happy
2637
+ * path exactly zero extra reads.
2638
+ *
2639
+ * An edge/CDN rule would be the other natural home and is deliberately not
2640
+ * the answer here: it is not a repo change, nothing in this suite could
2641
+ * verify it, and it cannot see which plan a host is on.
2642
+ */
2643
+ export declare function checkBandwidthAbuseCeiling(org: Partial<AglynOrgBilling> | null | undefined, usedPageViewsThisMonth: number): BandwidthAbuseCeilingResult;
2644
+ /**
2645
+ * Does crossing the ceiling **degrade what visitors see**, or only raise the
2646
+ * incident? (AGL-2155)
2647
+ *
2648
+ * Only on a plan that does NOT meter the infra pass-through — which today is
2649
+ * free/hobby, where the requirement is that free always actually stays free.
2650
+ * On free the traffic is uncompensated, so serving it is a
2651
+ * straight loss and the least destructive way to stop the bleeding is to stop
2652
+ * paying for the expensive render.
2653
+ *
2654
+ * On a metered plan the same traffic is *invoiced*, so taking a paying
2655
+ * customer's site off the air would trade a bill they agreed to for an outage
2656
+ * they did not. Their ceiling still trips, still flags the host and still
2657
+ * escalates to staff — it just does not change what a visitor gets. Whether a
2658
+ * paying host past its OWN ceiling should also be degraded is a product call
2659
+ * and is left open on purpose; flipping it is this one function.
2660
+ *
2661
+ * Enterprise does not meter either, and is NOT degraded, for the same reason
2662
+ * a metered plan is not: its traffic is paid for, by agreement rather than by
2663
+ * invoice. Since 2026-09-07 its bandwidth band is a finite fallback (twice
2664
+ * Agency's), so without this exclusion the ceiling an agreement never named
2665
+ * would have taken a contracted customer's site off the air three times past
2666
+ * a default nobody chose for them. It still trips, flags and pages.
2667
+ */
2668
+ export declare function bandwidthCeilingDegradesRender(org: Partial<AglynOrgBilling> | null | undefined): boolean;
2669
+ /**
2670
+ * `hosts/{id}.bandwidthCeiling` — the flag the beacon stamps and the render
2671
+ * path reads. Month-scoped so it **self-clears at the month boundary** with
2672
+ * no staff action and no write, the same property `LockdownState.untilMs`
2673
+ * has: a site contained in August serves normally on September 1.
2674
+ *
2675
+ * Admin-SDK-only. It is frozen against client writes in
2676
+ * `cloud/firebase-firestore.rules` alongside the `suspendedAt` family, for
2677
+ * the identical reason: a site that could clear its own containment does not
2678
+ * have one.
2679
+ */
2680
+ export interface HostBandwidthCeiling {
2681
+ /** `YYYY-MM` (UTC) the trip belongs to. */
2682
+ month: string;
2683
+ /** The ceiling that was crossed, in page views. */
2684
+ ceiling: number;
2685
+ /** The month count observed when it tripped. */
2686
+ used: number;
2687
+ trippedAtMs: number;
2688
+ /** False when the plan meters the overage — flagged, not degraded. */
2689
+ degraded: boolean;
2690
+ }
2691
+ /**
2692
+ * Read the host document's containment flag, tolerantly. Returns null unless
2693
+ * the flag is present, well-formed AND belongs to `month` — an August trip
2694
+ * must not silence a September visitor.
2695
+ */
2696
+ export declare function normalizeHostBandwidthCeiling(host: Record<string, any> | null | undefined, month: string): HostBandwidthCeiling | null;
2697
+ /**
2698
+ * Is the render path required to serve the capped notice instead of the page?
2699
+ *
2700
+ * Both halves must hold: the flag is for THIS month, and the trip was
2701
+ * recorded as one that degrades. `degraded` is written by the evaluator from
2702
+ * {@link bandwidthCeilingDegradesRender} at trip time rather than re-derived
2703
+ * here, so a host that upgrades mid-month is not still being degraded by a
2704
+ * flag written while it was free — and, in the other direction, a flag from a
2705
+ * paying host can never take that host down.
2706
+ */
2707
+ export declare function bandwidthCeilingDegradesHost(host: Record<string, any> | null | undefined, month: string): boolean;
2708
+ /** UTC `YYYY-MM`, the key both the beacon and the render path agree on. */
2709
+ export declare function bandwidthCeilingMonthKey(now?: Date): string;
2710
+ /**
2711
+ * Visitor-facing copy for a contained site. Says what happened and that it
2712
+ * ends, and names nothing about the plan, the count or the owner — this is
2713
+ * read by a stranger to the site, exactly like the form ceiling's refusal
2714
+ * body (AGL-1666).
2715
+ */
2716
+ export declare function bandwidthCeilingNotice(): {
2717
+ title: string;
2718
+ body: string;
2719
+ };
2720
+ /**
2721
+ * What a quota check costs a caller that intends to ENFORCE it (AGL-2163).
2722
+ *
2723
+ * `checkDataStorageQuota` and `checkApiRequestQuota` each carry an `allowed`
2724
+ * field, and their docblocks promise it refuses — "plans without one (free)
2725
+ * hard-block at the included size", "plans without API access have
2726
+ * `included: 0` and always block". Nothing read it. Both functions appeared
2727
+ * only in `/api/billing/report-usage`, which reads `overageMonthlyUsd` and
2728
+ * ignores `allowed`, so the promise was documentation and the free tier's
2729
+ * "runtime braces" on those two dimensions did not exist.
2730
+ *
2731
+ * Enforcing them naively means measuring first, and for dataset storage the
2732
+ * measurement is O(datasets) reads — far too much for a per-record write.
2733
+ * This makes the cheap answer available: for every plan shipping today the
2734
+ * verdict does not depend on the measurement at all, because a plan either
2735
+ * has an overage RATE (then it always allows and meters) or a band of zero
2736
+ * (then it always refuses). Measuring is required only for the shape a staff
2737
+ * `entitlementOverrides` can create — a finite, non-zero band on a plan with
2738
+ * no rate — and only then does the caller pay a read.
2739
+ *
2740
+ * - `'always-blocks'` — refuse now; no measurement, no read.
2741
+ * - `'never-blocks'` — proceed; no measurement, no read. **This is every
2742
+ * metered/paid plan**, so enforcement costs a paying customer nothing and
2743
+ * can never refuse them.
2744
+ * - `'measure'` — the verdict depends on usage; measure and call the check.
2745
+ */
2746
+ export type QuotaEnforcementShape = 'always-blocks' | 'never-blocks' | 'measure';
2747
+ /**
2748
+ * Enforcement shape of {@link checkDataStorageQuota} for this org, before
2749
+ * anything has been measured. @see QuotaEnforcementShape
2750
+ */
2751
+ export declare function dataStorageEnforcementShape(org: Partial<AglynOrgBilling> | null | undefined): QuotaEnforcementShape;
2752
+ /**
2753
+ * Enforcement shape of {@link checkApiRequestQuota} for this org, before
2754
+ * anything has been measured. @see QuotaEnforcementShape
2755
+ */
2756
+ export declare function apiRequestEnforcementShape(org: Partial<AglynOrgBilling> | null | undefined): QuotaEnforcementShape;