@aglyn/aglyn 1.0.0-beta.218 → 1.0.0-beta.220

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 (455) hide show
  1. package/package.json +12 -12
  2. package/src/lib/app-utils/actions.d.ts +26 -433
  3. package/src/lib/app-utils/actions.js +108 -633
  4. package/src/lib/app-utils/actions.js.map +1 -1
  5. package/src/lib/app-utils/activity-presenter.d.ts +3 -3
  6. package/src/lib/app-utils/activity-presenter.js +36 -52
  7. package/src/lib/app-utils/activity-presenter.js.map +1 -1
  8. package/src/lib/app-utils/analytics-events.d.ts +1 -1
  9. package/src/lib/app-utils/analytics-events.js.map +1 -1
  10. package/src/lib/app-utils/analytics-provider.d.ts +182 -0
  11. package/src/lib/app-utils/analytics-provider.js +133 -0
  12. package/src/lib/app-utils/analytics-provider.js.map +1 -0
  13. package/src/lib/app-utils/api-plugins.d.ts +46 -0
  14. package/src/lib/app-utils/api-plugins.js +19 -0
  15. package/src/lib/app-utils/api-plugins.js.map +1 -1
  16. package/src/lib/app-utils/{marketplace-overrides.d.ts → artifact-overrides.d.ts} +9 -6
  17. package/src/lib/app-utils/{marketplace-overrides.js → artifact-overrides.js} +10 -7
  18. package/src/lib/app-utils/artifact-overrides.js.map +1 -0
  19. package/src/lib/app-utils/{marketplace-provenance.d.ts → artifact-provenance.d.ts} +12 -10
  20. package/src/lib/app-utils/{marketplace-provenance.js → artifact-provenance.js} +9 -7
  21. package/src/lib/app-utils/artifact-provenance.js.map +1 -0
  22. package/src/lib/app-utils/binding-token-catalog.d.ts +5 -13
  23. package/src/lib/app-utils/binding-token-catalog.js +17 -41
  24. package/src/lib/app-utils/binding-token-catalog.js.map +1 -1
  25. package/src/lib/app-utils/brand-support-line.d.ts +36 -0
  26. package/src/lib/app-utils/brand-support-line.js +38 -0
  27. package/src/lib/app-utils/brand-support-line.js.map +1 -0
  28. package/src/lib/app-utils/collection-entries.d.ts +2 -2
  29. package/src/lib/app-utils/collection-entries.js.map +1 -1
  30. package/src/lib/app-utils/consent-group-change.d.ts +4 -0
  31. package/src/lib/app-utils/consent-group-change.js.map +1 -1
  32. package/src/lib/app-utils/consent-groups.d.ts +1 -1
  33. package/src/lib/app-utils/consent-groups.js.map +1 -1
  34. package/src/lib/app-utils/console-routes.d.ts +15 -42
  35. package/src/lib/app-utils/console-routes.js +10 -39
  36. package/src/lib/app-utils/console-routes.js.map +1 -1
  37. package/src/lib/app-utils/contacts.d.ts +24 -2
  38. package/src/lib/app-utils/contacts.js +21 -5
  39. package/src/lib/app-utils/contacts.js.map +1 -1
  40. package/src/lib/app-utils/container-membership.d.ts +93 -0
  41. package/src/lib/app-utils/container-membership.js +165 -0
  42. package/src/lib/app-utils/container-membership.js.map +1 -0
  43. package/src/lib/app-utils/crm-task-reminders.d.ts +23 -6
  44. package/src/lib/app-utils/crm-task-reminders.js +32 -11
  45. package/src/lib/app-utils/crm-task-reminders.js.map +1 -1
  46. package/src/lib/app-utils/crm.d.ts +24 -4
  47. package/src/lib/app-utils/crm.js +23 -0
  48. package/src/lib/app-utils/crm.js.map +1 -1
  49. package/src/lib/app-utils/cross-pool-desync.d.ts +39 -0
  50. package/src/lib/app-utils/cross-pool-desync.js +47 -0
  51. package/src/lib/app-utils/cross-pool-desync.js.map +1 -0
  52. package/src/lib/app-utils/csv-upload.d.ts +29 -0
  53. package/src/lib/app-utils/csv-upload.js +29 -0
  54. package/src/lib/app-utils/csv-upload.js.map +1 -0
  55. package/src/lib/app-utils/{dataset-csv.d.ts → csv.d.ts} +10 -43
  56. package/src/lib/app-utils/{dataset-csv.js → csv.js} +10 -81
  57. package/src/lib/app-utils/csv.js.map +1 -0
  58. package/src/lib/app-utils/dataset-models.d.ts +5 -5
  59. package/src/lib/app-utils/dataset-models.js +19 -2
  60. package/src/lib/app-utils/dataset-models.js.map +1 -1
  61. package/src/lib/app-utils/datasets.d.ts +65 -30
  62. package/src/lib/app-utils/datasets.js +74 -24
  63. package/src/lib/app-utils/datasets.js.map +1 -1
  64. package/src/lib/app-utils/docs-deflection.d.ts +6 -6
  65. package/src/lib/app-utils/docs-deflection.js.map +1 -1
  66. package/src/lib/app-utils/docs-help.generated.d.ts +14 -8
  67. package/src/lib/app-utils/docs-help.generated.js +23 -6
  68. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  69. package/src/lib/app-utils/docs-index.generated.d.ts +2 -2
  70. package/src/lib/app-utils/docs-index.generated.js +570 -346
  71. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  72. package/src/lib/app-utils/docs-retrieval.d.ts +3 -3
  73. package/src/lib/app-utils/docs-retrieval.js +14 -10
  74. package/src/lib/app-utils/docs-retrieval.js.map +1 -1
  75. package/src/lib/app-utils/duplicate-resource.d.ts +4 -1
  76. package/src/lib/app-utils/duplicate-resource.js +7 -4
  77. package/src/lib/app-utils/duplicate-resource.js.map +1 -1
  78. package/src/lib/app-utils/dynamic-list-rule.d.ts +1 -1
  79. package/src/lib/app-utils/dynamic-list-rule.js +3 -3
  80. package/src/lib/app-utils/dynamic-list-rule.js.map +1 -1
  81. package/src/lib/app-utils/element-ui.d.ts +1 -1
  82. package/src/lib/app-utils/element-ui.js +1 -1
  83. package/src/lib/app-utils/element-ui.js.map +1 -1
  84. package/src/lib/app-utils/email-starter-policy.js +3 -3
  85. package/src/lib/app-utils/email-starter-policy.js.map +1 -1
  86. package/src/lib/app-utils/email-text.d.ts +80 -0
  87. package/src/lib/app-utils/email-text.js +189 -0
  88. package/src/lib/app-utils/email-text.js.map +1 -0
  89. package/src/lib/app-utils/{list-assignment-policy.d.ts → enrollment-basis.d.ts} +4 -0
  90. package/src/lib/app-utils/{list-assignment-policy.js → enrollment-basis.js} +5 -1
  91. package/src/lib/app-utils/enrollment-basis.js.map +1 -0
  92. package/src/lib/app-utils/entity-picker-context.d.ts +10 -5
  93. package/src/lib/app-utils/entity-picker-context.js.map +1 -1
  94. package/src/lib/app-utils/error-beacon.d.ts +75 -6
  95. package/src/lib/app-utils/error-beacon.js +160 -21
  96. package/src/lib/app-utils/error-beacon.js.map +1 -1
  97. package/src/lib/app-utils/expand-repeatables.d.ts +10 -10
  98. package/src/lib/app-utils/expand-repeatables.js +14 -14
  99. package/src/lib/app-utils/expand-repeatables.js.map +1 -1
  100. package/src/lib/app-utils/forms.js +2 -2
  101. package/src/lib/app-utils/forms.js.map +1 -1
  102. package/src/lib/app-utils/health-report.d.ts +2 -3
  103. package/src/lib/app-utils/health-report.js +8 -6
  104. package/src/lib/app-utils/health-report.js.map +1 -1
  105. package/src/lib/app-utils/host-events.d.ts +82 -0
  106. package/src/lib/app-utils/host-events.js +62 -0
  107. package/src/lib/app-utils/host-events.js.map +1 -0
  108. package/src/lib/app-utils/inbound-mail-domain.d.ts +27 -0
  109. package/src/lib/app-utils/inbound-mail-domain.js +31 -0
  110. package/src/lib/app-utils/inbound-mail-domain.js.map +1 -0
  111. package/src/lib/app-utils/lockdown.d.ts +11 -0
  112. package/src/lib/app-utils/lockdown.js +33 -3
  113. package/src/lib/app-utils/lockdown.js.map +1 -1
  114. package/src/lib/app-utils/margin-utilization.d.ts +4 -69
  115. package/src/lib/app-utils/margin-utilization.js +82 -69
  116. package/src/lib/app-utils/margin-utilization.js.map +1 -1
  117. package/src/lib/app-utils/member-email-aliases.d.ts +25 -0
  118. package/src/lib/app-utils/member-email-aliases.js +29 -0
  119. package/src/lib/app-utils/member-email-aliases.js.map +1 -1
  120. package/src/lib/app-utils/node-definition-sanitizer.d.ts +16 -16
  121. package/src/lib/app-utils/node-definition-sanitizer.js +24 -20
  122. package/src/lib/app-utils/node-definition-sanitizer.js.map +1 -1
  123. package/src/lib/app-utils/node-interactions.d.ts +7 -7
  124. package/src/lib/app-utils/node-interactions.js.map +1 -1
  125. package/src/lib/app-utils/notifications.d.ts +147 -47
  126. package/src/lib/app-utils/notifications.js +182 -44
  127. package/src/lib/app-utils/notifications.js.map +1 -1
  128. package/src/lib/app-utils/operator-alerts.d.ts +15 -1
  129. package/src/lib/app-utils/operator-alerts.js +47 -10
  130. package/src/lib/app-utils/operator-alerts.js.map +1 -1
  131. package/src/lib/app-utils/payments-configured.d.ts +8 -21
  132. package/src/lib/app-utils/payments-configured.js +8 -23
  133. package/src/lib/app-utils/payments-configured.js.map +1 -1
  134. package/src/lib/app-utils/plan-entitlements.d.ts +207 -306
  135. package/src/lib/app-utils/plan-entitlements.js +345 -528
  136. package/src/lib/app-utils/plan-entitlements.js.map +1 -1
  137. package/src/lib/app-utils/platform-brand.d.ts +10 -0
  138. package/src/lib/app-utils/platform-brand.js +14 -3
  139. package/src/lib/app-utils/platform-brand.js.map +1 -1
  140. package/src/lib/app-utils/platform-marketing-consent.d.ts +56 -2
  141. package/src/lib/app-utils/platform-marketing-consent.js +6 -5
  142. package/src/lib/app-utils/platform-marketing-consent.js.map +1 -1
  143. package/src/lib/app-utils/plugin-api-rate-limit.d.ts +7 -1
  144. package/src/lib/app-utils/plugin-api-rate-limit.js +11 -3
  145. package/src/lib/app-utils/plugin-api-rate-limit.js.map +1 -1
  146. package/src/lib/app-utils/plugin-bundle-checks.js +2 -2
  147. package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -1
  148. package/src/lib/app-utils/plugin-host-events.generated.d.ts +12 -0
  149. package/src/lib/app-utils/plugin-host-events.generated.js +169 -0
  150. package/src/lib/app-utils/plugin-host-events.generated.js.map +1 -0
  151. package/src/lib/app-utils/plugin-release-flags.generated.d.ts +12 -0
  152. package/src/lib/app-utils/plugin-release-flags.generated.js +106 -0
  153. package/src/lib/app-utils/plugin-release-flags.generated.js.map +1 -0
  154. package/src/lib/app-utils/plugin-starter-templates.generated.d.ts +12 -0
  155. package/src/lib/app-utils/plugin-starter-templates.generated.js +711 -0
  156. package/src/lib/app-utils/plugin-starter-templates.generated.js.map +1 -0
  157. package/src/lib/app-utils/realm-host-surface.generated.js +2 -0
  158. package/src/lib/app-utils/realm-host-surface.generated.js.map +1 -1
  159. package/src/lib/app-utils/release-flags.d.ts +14 -5
  160. package/src/lib/app-utils/release-flags.js +21 -125
  161. package/src/lib/app-utils/release-flags.js.map +1 -1
  162. package/src/lib/app-utils/repeat-query.d.ts +56 -0
  163. package/src/lib/app-utils/{dataset-query.js → repeat-query.js} +16 -16
  164. package/src/lib/app-utils/repeat-query.js.map +1 -0
  165. package/src/lib/app-utils/repeat-sources.d.ts +9 -0
  166. package/src/lib/app-utils/repeat-sources.js.map +1 -1
  167. package/src/lib/app-utils/retained-refusals.d.ts +73 -0
  168. package/src/lib/app-utils/retained-refusals.js +186 -0
  169. package/src/lib/app-utils/retained-refusals.js.map +1 -0
  170. package/src/lib/app-utils/scope-tokens.d.ts +27 -0
  171. package/src/lib/app-utils/scope-tokens.js +61 -0
  172. package/src/lib/app-utils/scope-tokens.js.map +1 -1
  173. package/src/lib/app-utils/screen-link-context.js +3 -3
  174. package/src/lib/app-utils/screen-link-context.js.map +1 -1
  175. package/src/lib/app-utils/screen-route.js +1 -1
  176. package/src/lib/app-utils/screen-route.js.map +1 -1
  177. package/src/lib/app-utils/seo-audit.d.ts +132 -0
  178. package/src/lib/app-utils/seo-audit.js +246 -0
  179. package/src/lib/app-utils/seo-audit.js.map +1 -0
  180. package/src/lib/app-utils/seo-keywords.d.ts +48 -0
  181. package/src/lib/app-utils/seo-keywords.js +60 -0
  182. package/src/lib/app-utils/seo-keywords.js.map +1 -0
  183. package/src/lib/app-utils/seo-page-facts.d.ts +66 -0
  184. package/src/lib/app-utils/seo-page-facts.js +155 -0
  185. package/src/lib/app-utils/seo-page-facts.js.map +1 -0
  186. package/src/lib/app-utils/seo-site-scan.d.ts +126 -0
  187. package/src/lib/app-utils/seo-site-scan.js +162 -0
  188. package/src/lib/app-utils/seo-site-scan.js.map +1 -0
  189. package/src/lib/app-utils/seo-title-variables.js +1 -1
  190. package/src/lib/app-utils/seo-title-variables.js.map +1 -1
  191. package/src/lib/app-utils/server.d.ts +13 -18
  192. package/src/lib/app-utils/server.js +33 -51
  193. package/src/lib/app-utils/server.js.map +1 -1
  194. package/src/lib/app-utils/site-interactions.d.ts +501 -0
  195. package/src/lib/app-utils/site-interactions.js +554 -0
  196. package/src/lib/app-utils/site-interactions.js.map +1 -0
  197. package/src/lib/app-utils/{marketplace-theme.d.ts → site-theme.d.ts} +13 -2
  198. package/src/lib/app-utils/{marketplace-theme.js → site-theme.js} +2 -2
  199. package/src/lib/app-utils/site-theme.js.map +1 -0
  200. package/src/lib/app-utils/sitemap.d.ts +4 -3
  201. package/src/lib/app-utils/sitemap.js +9 -7
  202. package/src/lib/app-utils/sitemap.js.map +1 -1
  203. package/src/lib/app-utils/stale-build-error.d.ts +51 -0
  204. package/src/lib/app-utils/stale-build-error.js +100 -1
  205. package/src/lib/app-utils/stale-build-error.js.map +1 -1
  206. package/src/lib/app-utils/starter-template-nodes.d.ts +137 -0
  207. package/src/lib/app-utils/starter-template-nodes.js +129 -0
  208. package/src/lib/app-utils/starter-template-nodes.js.map +1 -0
  209. package/src/lib/app-utils/starter-templates.d.ts +11 -34
  210. package/src/lib/app-utils/starter-templates.js +19 -230
  211. package/src/lib/app-utils/starter-templates.js.map +1 -1
  212. package/src/lib/app-utils/{email-topics.d.ts → subscription-topics.d.ts} +64 -48
  213. package/src/lib/app-utils/{email-topics.js → subscription-topics.js} +39 -109
  214. package/src/lib/app-utils/subscription-topics.js.map +1 -0
  215. package/src/lib/app-utils/tax-jurisdiction-figures.d.ts +118 -0
  216. package/src/lib/app-utils/tax-jurisdiction-figures.js +123 -0
  217. package/src/lib/app-utils/tax-jurisdiction-figures.js.map +1 -0
  218. package/src/lib/app-utils/theme-library.d.ts +10 -3
  219. package/src/lib/app-utils/theme-library.js +13 -4
  220. package/src/lib/app-utils/theme-library.js.map +1 -1
  221. package/src/lib/app-utils/usage-budget.d.ts +73 -188
  222. package/src/lib/app-utils/usage-budget.js +58 -194
  223. package/src/lib/app-utils/usage-budget.js.map +1 -1
  224. package/src/lib/app-utils/{campaign-attribution.d.ts → utm-attribution.d.ts} +11 -6
  225. package/src/lib/app-utils/{campaign-attribution.js → utm-attribution.js} +13 -8
  226. package/src/lib/app-utils/utm-attribution.js.map +1 -0
  227. package/src/lib/app-utils/{campaign-forwarding.d.ts → utm-forwarding.d.ts} +14 -14
  228. package/src/lib/app-utils/{campaign-forwarding.js → utm-forwarding.js} +26 -26
  229. package/src/lib/app-utils/utm-forwarding.js.map +1 -0
  230. package/src/lib/app-utils/{campaign-touch.d.ts → utm-touch.d.ts} +25 -17
  231. package/src/lib/app-utils/{campaign-touch.js → utm-touch.js} +38 -30
  232. package/src/lib/app-utils/utm-touch.js.map +1 -0
  233. package/src/lib/app-utils/variables.d.ts +9 -9
  234. package/src/lib/app-utils/variables.js.map +1 -1
  235. package/src/lib/app-utils/video-object.js +6 -5
  236. package/src/lib/app-utils/video-object.js.map +1 -1
  237. package/src/lib/app-utils/visitor-consent.d.ts +41 -164
  238. package/src/lib/app-utils/visitor-consent.js +66 -277
  239. package/src/lib/app-utils/visitor-consent.js.map +1 -1
  240. package/src/lib/app-utils/visitor-record-ceiling.d.ts +32 -0
  241. package/src/lib/app-utils/visitor-record-ceiling.js +39 -0
  242. package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -1
  243. package/src/lib/app-utils/web-vitals-rum.d.ts +7 -7
  244. package/src/lib/app-utils/web-vitals-rum.js +31 -63
  245. package/src/lib/app-utils/web-vitals-rum.js.map +1 -1
  246. package/src/lib/app-utils/where-used.d.ts +11 -6
  247. package/src/lib/app-utils/where-used.js +3 -8
  248. package/src/lib/app-utils/where-used.js.map +1 -1
  249. package/src/lib/foundation/definitions/components.types.d.ts +1 -1
  250. package/src/lib/foundation/definitions/components.types.js +1 -1
  251. package/src/lib/foundation/definitions/components.types.js.map +1 -1
  252. package/src/lib/foundation/definitions/org-billing.types.d.ts +11 -137
  253. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
  254. package/src/lib/foundation/definitions/organization.types.d.ts +1 -3
  255. package/src/lib/foundation/definitions/organization.types.js.map +1 -1
  256. package/src/lib/foundation/definitions/platform.types.d.ts +11 -5
  257. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  258. package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +16 -0
  259. package/src/lib/foundation/definitions/write-deny-coverage.util.js +20 -0
  260. package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -1
  261. package/src/lib/foundation/index.d.ts +1 -1
  262. package/src/lib/foundation/index.js.map +1 -1
  263. package/src/lib/plugin-manager/besigner-documents.d.ts +96 -0
  264. package/src/lib/plugin-manager/besigner-documents.js +61 -0
  265. package/src/lib/plugin-manager/besigner-documents.js.map +1 -0
  266. package/src/lib/plugin-manager/computed-variables.d.ts +71 -0
  267. package/src/lib/plugin-manager/computed-variables.js +74 -0
  268. package/src/lib/plugin-manager/computed-variables.js.map +1 -0
  269. package/src/lib/plugin-manager/custom-fields.d.ts +29 -2
  270. package/src/lib/plugin-manager/custom-fields.js +47 -1
  271. package/src/lib/plugin-manager/custom-fields.js.map +1 -1
  272. package/src/lib/plugin-manager/feature-plugins.d.ts +372 -99
  273. package/src/lib/plugin-manager/feature-plugins.js +126 -51
  274. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  275. package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +134 -0
  276. package/src/lib/plugin-manager/first-party-plugins.generated.js +840 -10
  277. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  278. package/src/lib/plugin-manager/index.d.ts +0 -1
  279. package/src/lib/plugin-manager/index.js +0 -1
  280. package/src/lib/plugin-manager/index.js.map +1 -1
  281. package/src/lib/plugin-manager/plugin-activity-actions.d.ts +12 -0
  282. package/src/lib/plugin-manager/plugin-activity-actions.js +25 -0
  283. package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -1
  284. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +9 -4
  285. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
  286. package/src/lib/plugin-manager/plugin-containers.d.ts +68 -0
  287. package/src/lib/plugin-manager/plugin-containers.generated.d.ts +10 -0
  288. package/src/lib/plugin-manager/plugin-containers.generated.js +20 -0
  289. package/src/lib/plugin-manager/plugin-containers.generated.js.map +1 -0
  290. package/src/lib/plugin-manager/plugin-containers.js +27 -0
  291. package/src/lib/plugin-manager/plugin-containers.js.map +1 -0
  292. package/src/lib/plugin-manager/plugin-contributions.d.ts +1 -1
  293. package/src/lib/plugin-manager/plugin-contributions.js.map +1 -1
  294. package/src/lib/plugin-manager/plugin-declarations-repair.d.ts +36 -0
  295. package/src/lib/plugin-manager/plugin-declarations-repair.js +64 -0
  296. package/src/lib/plugin-manager/plugin-declarations-repair.js.map +1 -0
  297. package/src/lib/plugin-manager/plugin-dependents.d.ts +101 -0
  298. package/src/lib/plugin-manager/plugin-dependents.js +75 -0
  299. package/src/lib/plugin-manager/plugin-dependents.js.map +1 -0
  300. package/src/lib/plugin-manager/plugin-distribution.d.ts +55 -0
  301. package/src/lib/plugin-manager/plugin-distribution.js +45 -0
  302. package/src/lib/plugin-manager/plugin-distribution.js.map +1 -0
  303. package/src/lib/plugin-manager/plugin-entitlements.d.ts +8 -0
  304. package/src/lib/plugin-manager/plugin-entitlements.js.map +1 -1
  305. package/src/lib/plugin-manager/plugin-host-collections.d.ts +17 -0
  306. package/src/lib/plugin-manager/plugin-host-collections.js +6 -0
  307. package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
  308. package/src/lib/plugin-manager/plugin-host-resources.d.ts +133 -0
  309. package/src/lib/plugin-manager/plugin-host-resources.js +52 -0
  310. package/src/lib/plugin-manager/plugin-host-resources.js.map +1 -0
  311. package/src/lib/plugin-manager/plugin-membership-detach.js +1 -1
  312. package/src/lib/plugin-manager/plugin-membership-detach.js.map +1 -1
  313. package/src/lib/plugin-manager/plugin-org-erasure.d.ts +28 -2
  314. package/src/lib/plugin-manager/plugin-org-erasure.js +61 -2
  315. package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -1
  316. package/src/lib/plugin-manager/plugin-plan-entitlements.d.ts +123 -0
  317. package/src/lib/plugin-manager/plugin-plan-entitlements.js +101 -0
  318. package/src/lib/plugin-manager/plugin-plan-entitlements.js.map +1 -0
  319. package/src/lib/plugin-manager/plugin-record-email-state.d.ts +69 -0
  320. package/src/lib/plugin-manager/plugin-record-email-state.js +32 -0
  321. package/src/lib/plugin-manager/plugin-record-email-state.js.map +1 -1
  322. package/src/lib/plugin-manager/plugin-record-index.d.ts +95 -0
  323. package/src/lib/plugin-manager/plugin-record-index.js +51 -0
  324. package/src/lib/plugin-manager/plugin-record-index.js.map +1 -0
  325. package/src/lib/plugin-manager/plugin-record-lists.d.ts +115 -0
  326. package/src/lib/plugin-manager/plugin-record-lists.js +75 -0
  327. package/src/lib/plugin-manager/plugin-record-lists.js.map +1 -0
  328. package/src/lib/plugin-manager/plugin-record-timeline.d.ts +42 -8
  329. package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -1
  330. package/src/lib/plugin-manager/plugin-record-written.d.ts +58 -0
  331. package/src/lib/plugin-manager/plugin-record-written.js +42 -0
  332. package/src/lib/plugin-manager/plugin-record-written.js.map +1 -0
  333. package/src/lib/plugin-manager/plugin-revenue-sources.d.ts +213 -0
  334. package/src/lib/plugin-manager/plugin-revenue-sources.js +194 -0
  335. package/src/lib/plugin-manager/plugin-revenue-sources.js.map +1 -0
  336. package/src/lib/plugin-manager/plugin-site-beacons.d.ts +71 -0
  337. package/src/lib/plugin-manager/plugin-site-beacons.js +54 -0
  338. package/src/lib/plugin-manager/plugin-site-beacons.js.map +1 -0
  339. package/src/lib/plugin-manager/plugin-site-bundle.d.ts +145 -0
  340. package/src/lib/plugin-manager/plugin-site-bundle.js +78 -0
  341. package/src/lib/plugin-manager/plugin-site-bundle.js.map +1 -0
  342. package/src/lib/plugin-manager/plugin-site-cache.d.ts +12 -0
  343. package/src/lib/plugin-manager/plugin-site-cache.js.map +1 -1
  344. package/src/lib/plugin-manager/plugin-site-export.d.ts +81 -0
  345. package/src/lib/plugin-manager/plugin-site-export.js +40 -0
  346. package/src/lib/plugin-manager/plugin-site-export.js.map +1 -0
  347. package/src/lib/plugin-manager/plugin-sitemap-sections.d.ts +88 -0
  348. package/src/lib/plugin-manager/plugin-sitemap-sections.js +29 -0
  349. package/src/lib/plugin-manager/plugin-sitemap-sections.js.map +1 -0
  350. package/src/lib/plugin-manager/plugin-subscription-topics.generated.d.ts +11 -0
  351. package/src/lib/plugin-manager/plugin-subscription-topics.generated.js +41 -0
  352. package/src/lib/plugin-manager/plugin-subscription-topics.generated.js.map +1 -0
  353. package/src/lib/plugin-manager/plugin-tax-profile.d.ts +20 -4
  354. package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -1
  355. package/src/lib/plugin-manager/plugin-tax-return-sources.d.ts +255 -0
  356. package/src/lib/plugin-manager/plugin-tax-return-sources.js +191 -0
  357. package/src/lib/plugin-manager/plugin-tax-return-sources.js.map +1 -0
  358. package/src/lib/plugin-manager/plugin-template-sources.d.ts +75 -0
  359. package/src/lib/plugin-manager/plugin-template-sources.js +37 -0
  360. package/src/lib/plugin-manager/plugin-template-sources.js.map +1 -0
  361. package/src/lib/plugin-manager/plugin-usage-axes.d.ts +319 -0
  362. package/src/lib/plugin-manager/plugin-usage-axes.js +186 -0
  363. package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -0
  364. package/src/lib/plugin-manager/plugin-usage-meters.d.ts +108 -0
  365. package/src/lib/plugin-manager/plugin-usage-meters.js +105 -0
  366. package/src/lib/plugin-manager/plugin-usage-meters.js.map +1 -0
  367. package/src/lib/plugin-manager/plugin-zones.d.ts +4 -5
  368. package/src/lib/plugin-manager/plugin-zones.js.map +1 -1
  369. package/src/lib/plugin-manager/realm-host-aglyn.generated.js +2 -0
  370. package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -1
  371. package/src/lib/plugin-manager/record-captured-contact.d.ts +1 -14
  372. package/src/lib/plugin-manager/record-captured-contact.js +10 -32
  373. package/src/lib/plugin-manager/record-captured-contact.js.map +1 -1
  374. package/src/lib/plugin-manager/repeat-rows.d.ts +124 -0
  375. package/src/lib/plugin-manager/repeat-rows.js +108 -0
  376. package/src/lib/plugin-manager/repeat-rows.js.map +1 -0
  377. package/src/lib/plugin-manager/submission-record-target.d.ts +144 -0
  378. package/src/lib/plugin-manager/submission-record-target.js +125 -0
  379. package/src/lib/plugin-manager/submission-record-target.js.map +1 -0
  380. package/src/lib/plugin-manager/usage-alert-contributors.d.ts +76 -6
  381. package/src/lib/plugin-manager/usage-alert-contributors.js +23 -10
  382. package/src/lib/plugin-manager/usage-alert-contributors.js.map +1 -1
  383. package/src/lib/plugin-manager/video-embed-provider.d.ts +108 -0
  384. package/src/lib/plugin-manager/video-embed-provider.js +165 -0
  385. package/src/lib/plugin-manager/video-embed-provider.js.map +1 -0
  386. package/src/lib/types/nodes.d.ts +2 -2
  387. package/src/lib/types/nodes.js.map +1 -1
  388. package/src/lib/app-utils/assist-credits.d.ts +0 -482
  389. package/src/lib/app-utils/assist-credits.js +0 -509
  390. package/src/lib/app-utils/assist-credits.js.map +0 -1
  391. package/src/lib/app-utils/campaign-attribution.js.map +0 -1
  392. package/src/lib/app-utils/campaign-forwarding.js.map +0 -1
  393. package/src/lib/app-utils/campaign-membership.d.ts +0 -121
  394. package/src/lib/app-utils/campaign-membership.js +0 -189
  395. package/src/lib/app-utils/campaign-membership.js.map +0 -1
  396. package/src/lib/app-utils/campaign-touch.js.map +0 -1
  397. package/src/lib/app-utils/console-record-links.d.ts +0 -163
  398. package/src/lib/app-utils/console-record-links.js +0 -180
  399. package/src/lib/app-utils/console-record-links.js.map +0 -1
  400. package/src/lib/app-utils/contact-holder.d.ts +0 -68
  401. package/src/lib/app-utils/contact-holder.js +0 -129
  402. package/src/lib/app-utils/contact-holder.js.map +0 -1
  403. package/src/lib/app-utils/contact-merge.d.ts +0 -136
  404. package/src/lib/app-utils/contact-merge.js +0 -412
  405. package/src/lib/app-utils/contact-merge.js.map +0 -1
  406. package/src/lib/app-utils/crm-booking.d.ts +0 -75
  407. package/src/lib/app-utils/crm-booking.js +0 -133
  408. package/src/lib/app-utils/crm-booking.js.map +0 -1
  409. package/src/lib/app-utils/crm-csv.d.ts +0 -239
  410. package/src/lib/app-utils/crm-csv.js +0 -467
  411. package/src/lib/app-utils/crm-csv.js.map +0 -1
  412. package/src/lib/app-utils/crm-digest.d.ts +0 -185
  413. package/src/lib/app-utils/crm-digest.js +0 -272
  414. package/src/lib/app-utils/crm-digest.js.map +0 -1
  415. package/src/lib/app-utils/crm-inbound.d.ts +0 -245
  416. package/src/lib/app-utils/crm-inbound.js +0 -407
  417. package/src/lib/app-utils/crm-inbound.js.map +0 -1
  418. package/src/lib/app-utils/crm-reports.d.ts +0 -479
  419. package/src/lib/app-utils/crm-reports.js +0 -680
  420. package/src/lib/app-utils/crm-reports.js.map +0 -1
  421. package/src/lib/app-utils/dataset-csv.js.map +0 -1
  422. package/src/lib/app-utils/dataset-query.d.ts +0 -57
  423. package/src/lib/app-utils/dataset-query.js.map +0 -1
  424. package/src/lib/app-utils/email-topics.js.map +0 -1
  425. package/src/lib/app-utils/form-dataset-binding.d.ts +0 -50
  426. package/src/lib/app-utils/form-dataset-binding.js +0 -74
  427. package/src/lib/app-utils/form-dataset-binding.js.map +0 -1
  428. package/src/lib/app-utils/list-assignment-policy.js.map +0 -1
  429. package/src/lib/app-utils/list-import.d.ts +0 -158
  430. package/src/lib/app-utils/list-import.js +0 -332
  431. package/src/lib/app-utils/list-import.js.map +0 -1
  432. package/src/lib/app-utils/marketplace-listing-visibility.d.ts +0 -122
  433. package/src/lib/app-utils/marketplace-listing-visibility.js +0 -147
  434. package/src/lib/app-utils/marketplace-listing-visibility.js.map +0 -1
  435. package/src/lib/app-utils/marketplace-overrides.js.map +0 -1
  436. package/src/lib/app-utils/marketplace-provenance.js.map +0 -1
  437. package/src/lib/app-utils/marketplace-theme.js.map +0 -1
  438. package/src/lib/app-utils/marketplace-update-state.d.ts +0 -119
  439. package/src/lib/app-utils/marketplace-update-state.js +0 -132
  440. package/src/lib/app-utils/marketplace-update-state.js.map +0 -1
  441. package/src/lib/app-utils/publisher-agreement.d.ts +0 -289
  442. package/src/lib/app-utils/publisher-agreement.js +0 -321
  443. package/src/lib/app-utils/publisher-agreement.js.map +0 -1
  444. package/src/lib/app-utils/wistia-embed.d.ts +0 -104
  445. package/src/lib/app-utils/wistia-embed.js +0 -138
  446. package/src/lib/app-utils/wistia-embed.js.map +0 -1
  447. package/src/lib/app-utils/workflows.d.ts +0 -136
  448. package/src/lib/app-utils/workflows.js +0 -346
  449. package/src/lib/app-utils/workflows.js.map +0 -1
  450. package/src/lib/plugin-manager/commerce-zone-props.d.ts +0 -172
  451. package/src/lib/plugin-manager/commerce-zone-props.js +0 -31
  452. package/src/lib/plugin-manager/commerce-zone-props.js.map +0 -1
  453. package/src/lib/plugin-manager/plugin-metered-lines.d.ts +0 -97
  454. package/src/lib/plugin-manager/plugin-metered-lines.js +0 -123
  455. package/src/lib/plugin-manager/plugin-metered-lines.js.map +0 -1
@@ -14,15 +14,15 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */ /**
17
- * Dataset query layer (AGL-181): filter/sort/limit over typed documents,
18
- * shared by the console editor and the tenant renderer's repeatable
19
- * expansion. Deliberately evaluated IN MEMORY over the already-bounded
20
- * reads (editor page / `REPEAT_MAX_RECORDS`) — the Firestore reality
21
- * check from the issue: this sidesteps inequality-filter and composite-
22
- * index constraints entirely, at the documented cost that documents
23
- * beyond the fetch bound are never considered. The bound is explicit,
24
- * never silently unbounded.
25
- */ export const DATASET_QUERY_OPS = [
17
+ * A repeat's query (AGL-181): the filter, sort and limit an element's
18
+ * `repeatFilter` / `repeatSort` / `repeatLimit` apply to the rows it repeats
19
+ * over, whatever source supplied them — the canvas preview and the published
20
+ * page's expansion run the same one. Deliberately evaluated IN MEMORY over
21
+ * the already-bounded reads (`REPEAT_MAX_RECORDS`): this sidesteps
22
+ * inequality-filter and composite-index constraints entirely, at the
23
+ * documented cost that rows beyond the fetch bound are never considered. The
24
+ * bound is explicit, never silently unbounded.
25
+ */ export const REPEAT_QUERY_OPS = [
26
26
  '==',
27
27
  '!=',
28
28
  '>',
@@ -35,7 +35,7 @@
35
35
  * Parses the authoring shorthand `field op value` (e.g. `price <= 20`,
36
36
  * `tier == plus`, `tags contains red`). Null for unparseable input so
37
37
  * callers fail open (no filter) rather than filtering wrongly.
38
- */ export function parseDatasetFilter(input) {
38
+ */ export function parseRepeatFilter(input) {
39
39
  const match = String(input != null ? input : '').trim().match(/^([A-Za-z][A-Za-z0-9_]*)\s*(==|!=|>=|<=|>|<|contains)\s+(.+)$/);
40
40
  if (!match) return null;
41
41
  return {
@@ -44,7 +44,7 @@
44
44
  value: match[3].trim()
45
45
  };
46
46
  }
47
- /** Parses `field` or `field desc` into an orderBy config. */ export function parseDatasetSort(input) {
47
+ /** Parses `field` or `field desc` into an orderBy config. */ export function parseRepeatSort(input) {
48
48
  var _match_;
49
49
  const match = String(input != null ? input : '').trim().match(/^([A-Za-z][A-Za-z0-9_]*)(?:\s+(asc|desc))?$/i);
50
50
  if (!match) return null;
@@ -92,10 +92,10 @@ function matches(stored, where) {
92
92
  return false;
93
93
  }
94
94
  /**
95
- * Applies where/orderBy/limit to in-memory rows. Where-clauses on fields
96
- * absent from the model still run (stored-value comparison) so v1
97
- * datasets work; timestamps compare numerically (epoch millis storage).
98
- */ export function applyDatasetQuery(model, rows, query) {
95
+ * Applies where/orderBy/limit to in-memory rows. A clause compares the
96
+ * stored value whatever field it names, so rows with no declared field
97
+ * model filter too; timestamps compare numerically (epoch millis storage).
98
+ */ export function applyRepeatQuery(rows, query) {
99
99
  var _query_where;
100
100
  let result = rows;
101
101
  for (const where of (_query_where = query.where) != null ? _query_where : []){
@@ -123,4 +123,4 @@ function matches(stored, where) {
123
123
  return result;
124
124
  }
125
125
 
126
- //# sourceMappingURL=dataset-query.js.map
126
+ //# sourceMappingURL=repeat-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/repeat-query.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A repeat's query (AGL-181): the filter, sort and limit an element's\n * `repeatFilter` / `repeatSort` / `repeatLimit` apply to the rows it repeats\n * over, whatever source supplied them — the canvas preview and the published\n * page's expansion run the same one. Deliberately evaluated IN MEMORY over\n * the already-bounded reads (`REPEAT_MAX_RECORDS`): this sidesteps\n * inequality-filter and composite-index constraints entirely, at the\n * documented cost that rows beyond the fetch bound are never considered. The\n * bound is explicit, never silently unbounded.\n */\n\nexport type RepeatQueryOp =\n | '=='\n | '!='\n | '>'\n | '>='\n | '<'\n | '<='\n | 'contains'\n\nexport const REPEAT_QUERY_OPS: RepeatQueryOp[] = [\n '==',\n '!=',\n '>',\n '>=',\n '<',\n '<=',\n 'contains',\n]\n\nexport interface RepeatQueryWhere {\n fieldId: string\n op: RepeatQueryOp\n /** Literal, compared with type-aware coercion against stored values. */\n value: string\n}\n\nexport interface RepeatQuery {\n where?: RepeatQueryWhere[]\n orderBy?: { fieldId: string; direction?: 'asc' | 'desc' }\n limit?: number\n}\n\n/**\n * Parses the authoring shorthand `field op value` (e.g. `price <= 20`,\n * `tier == plus`, `tags contains red`). Null for unparseable input so\n * callers fail open (no filter) rather than filtering wrongly.\n */\nexport function parseRepeatFilter(input: string): RepeatQueryWhere | null {\n const match = String(input ?? '')\n .trim()\n .match(/^([A-Za-z][A-Za-z0-9_]*)\\s*(==|!=|>=|<=|>|<|contains)\\s+(.+)$/)\n if (!match) return null\n return {\n fieldId: match[1],\n op: match[2] as RepeatQueryOp,\n value: match[3].trim(),\n }\n}\n\n/** Parses `field` or `field desc` into an orderBy config. */\nexport function parseRepeatSort(\n input: string,\n): RepeatQuery['orderBy'] | null {\n const match = String(input ?? '')\n .trim()\n .match(/^([A-Za-z][A-Za-z0-9_]*)(?:\\s+(asc|desc))?$/i)\n if (!match) return null\n return {\n fieldId: match[1],\n direction: match[2]?.toLowerCase() === 'desc' ? 'desc' : 'asc',\n }\n}\n\nconst comparable = (value: unknown): number | string | null => {\n if (typeof value === 'number') return value\n if (typeof value === 'boolean') return value ? 1 : 0\n if (typeof value === 'string') {\n const numeric = Number(value)\n return Number.isFinite(numeric) && value.trim() !== '' ? numeric : value\n }\n return null\n}\n\nfunction matches(stored: unknown, where: RepeatQueryWhere): boolean {\n const { op, value } = where\n if (op === 'contains') {\n if (Array.isArray(stored)) {\n return stored.some(\n (entry) => String(entry).toLowerCase() === value.toLowerCase(),\n )\n }\n return String(stored ?? '')\n .toLowerCase()\n .includes(value.toLowerCase())\n }\n if (op === '==' || op === '!=') {\n const equal =\n typeof stored === 'boolean'\n ? String(stored) === value.toLowerCase()\n : String(stored ?? '') === value\n return op === '==' ? equal : !equal\n }\n const left = comparable(stored)\n const right = comparable(value)\n if (left == null || right == null || typeof left !== typeof right) {\n return false\n }\n switch (op) {\n case '>':\n return left > right\n case '>=':\n return left >= right\n case '<':\n return left < right\n case '<=':\n return left <= right\n }\n return false\n}\n\n/**\n * Applies where/orderBy/limit to in-memory rows. A clause compares the\n * stored value whatever field it names, so rows with no declared field\n * model filter too; timestamps compare numerically (epoch millis storage).\n */\nexport function applyRepeatQuery<Row extends Record<string, unknown>>(\n rows: Row[],\n query: RepeatQuery,\n): Row[] {\n let result = rows\n for (const where of query.where ?? []) {\n result = result.filter((row) => matches(row[where.fieldId], where))\n }\n if (query.orderBy) {\n const { fieldId, direction } = query.orderBy\n const sign = direction === 'desc' ? -1 : 1\n result = [...result].sort((a, b) => {\n const left = comparable(a[fieldId])\n const right = comparable(b[fieldId])\n if (left == null) return 1\n if (right == null) return -1\n if (typeof left === 'number' && typeof right === 'number') {\n return (left - right) * sign\n }\n return String(left).localeCompare(String(right)) * sign\n })\n }\n if (query.limit != null && query.limit > 0) {\n result = result.slice(0, query.limit)\n }\n return result\n}\n"],"names":["REPEAT_QUERY_OPS","parseRepeatFilter","input","match","String","trim","fieldId","op","value","parseRepeatSort","direction","toLowerCase","comparable","numeric","Number","isFinite","matches","stored","where","Array","isArray","some","entry","includes","equal","left","right","applyRepeatQuery","rows","query","result","filter","row","orderBy","sign","sort","a","b","localeCompare","limit","slice"],"mappings":"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"}
@@ -15,6 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import type { AglynAttributeSchema } from '../foundation/definitions/components.types';
18
+ import type { EntityPickerKind } from './entity-picker-context';
18
19
  import type { RepeatableDataset } from './expand-repeatables';
19
20
  /**
20
21
  * Where a repeat's rows come from, for the editor (AGL-3111).
@@ -78,6 +79,14 @@ export interface RepeatSource {
78
79
  * component attribute is. The panel names the field after `keyProp`.
79
80
  */
80
81
  keyAttribute: Omit<AglynAttributeSchema, 'name'>;
82
+ /**
83
+ * The entity picker kind a key names, when a key is an entity the picker
84
+ * lists. With it, the insert-token menu inside the repeat names the entity
85
+ * through the picker and offers `{{item.<field>}}` for each field the
86
+ * entity declares (`EntityPickerContextValue.entityFields`). Without it the
87
+ * menu offers no item tokens and authors type them.
88
+ */
89
+ entityKind?: EntityPickerKind;
81
90
  /**
82
91
  * Reads one key's rows for the besigner's canvas preview and badge. A React
83
92
  * hook: the editor mounts one reader per key and calls it unconditionally,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/repeat-sources.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynAttributeSchema } from '../foundation/definitions/components.types'\nimport type { RepeatableDataset } from './expand-repeatables'\n\n/**\n * Where a repeat's rows come from, for the editor (AGL-3111).\n *\n * Any element can repeat, and the platform decides what a repeat MEANS — the\n * scope, the copies, the bounds (`expand-repeatables.ts`). What it does not\n * decide is what an element repeats OVER. That belongs to the plugin that owns\n * the rows, which registers a source here from the registration function its\n * loader calls by name. Core names no source.\n *\n * Reached by path, never through a barrel: nothing on a published page asks\n * where rows come from, so nothing here is in a published page's download.\n */\n\n/**\n * What a source answers for one key.\n *\n * - `loading` — no answer yet.\n * - `missing` — the key names nothing this site may use. The published page\n * renders the element once, as written.\n * - `error` — the read failed. Nothing is known about the rows.\n * - `ready` — the rows, read and bounded exactly as the published page's\n * composition reads them.\n */\nexport type RepeatRowsAnswer =\n | { status: 'loading' }\n | { status: 'missing' }\n | { status: 'error' }\n | {\n status: 'ready'\n /** The name authors know the entry by, for the canvas badge. */\n label: string\n /**\n * The rows under every key the expansion looks them up by: the key as\n * the node stores it, the entry's id, and each entry a one-hop reference\n * in the rows reads (`{{item.ref.field}}`).\n */\n rowsByKey: Readonly<Record<string, RepeatableDataset>>\n }\n\n/** One read a source is asked for. */\nexport interface RepeatRowsRequest {\n /** The site the canvas edits. A source answers only what it may show. */\n hostId: string\n /** The key exactly as the node stores it. */\n key: string\n}\n\n/** One kind of thing an element can repeat over. */\nexport interface RepeatSource {\n /** Stable id, e.g. the plugin's name for the kind. */\n id: string\n /** What the Attributes panel calls the kind. */\n label: string\n /**\n * The node prop the chosen key persists under. Persisted in documents, so a\n * source never renames it.\n */\n keyProp: string\n /**\n * The attribute that picks a key, drawn by the Attributes panel as any\n * component attribute is. The panel names the field after `keyProp`.\n */\n keyAttribute: Omit<AglynAttributeSchema, 'name'>\n /**\n * Reads one key's rows for the besigner's canvas preview and badge. A React\n * hook: the editor mounts one reader per key and calls it unconditionally,\n * so its identity must not change after registration.\n */\n useRows(request: RepeatRowsRequest): RepeatRowsAnswer\n}\n\nconst sources = new Map<string, RepeatSource>()\nconst listeners = new Set<() => void>()\nlet version = 0\n/**\n * Replaced only when the registered set changes, never rebuilt per call: it is\n * the snapshot `useSyncExternalStore` compares, and a fresh array every time\n * is a render loop rather than a wasted allocation.\n */\nlet snapshot: readonly RepeatSource[] = []\n\nconst notify = () => {\n version += 1\n snapshot = [...sources.values()]\n for (const listener of [...listeners]) listener()\n}\n\n/**\n * Registers a source, replacing any registered under its id. Returns the\n * unregister, which removes the source only if it is still the one registered.\n *\n * Call it from a registration function the plugin loader calls BY NAME, never\n * from a module's top level: a bundler deletes a module imported only for its\n * side effect when its package says it has none (AGL-3025).\n */\nexport function registerRepeatSource(source: RepeatSource): () => void {\n if (sources.get(source.id) !== source) {\n sources.set(source.id, source)\n notify()\n }\n return () => {\n if (sources.get(source.id) !== source) return\n sources.delete(source.id)\n notify()\n }\n}\n\n/**\n * Every registered source, in registration order. The SAME array until the\n * registered set changes, so it can be read as a store snapshot.\n */\nexport function listRepeatSources(): readonly RepeatSource[] {\n return snapshot\n}\n\n/** The source registered under `id`. */\nexport function getRepeatSource(id: string): RepeatSource | undefined {\n return sources.get(id)\n}\n\n/** Called after the registered set changes. Returns the unsubscribe. */\nexport function subscribeRepeatSources(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** Moves on every change — the snapshot `useSyncExternalStore` reads. */\nexport function getRepeatSourcesVersion(): number {\n return version\n}\n\n/**\n * The registered source a node repeats over, and the key it names, trimmed.\n * `undefined` for a node that repeats over nothing a registered source owns.\n */\nexport function repeatSourceOf(\n node: unknown,\n): { source: RepeatSource; key: string } | undefined {\n const props = (node as { props?: Record<string, unknown> } | undefined)?.props\n if (!props) return undefined\n for (const source of sources.values()) {\n const value = props[source.keyProp]\n const key = typeof value === 'string' ? value.trim() : ''\n if (key) return { source, key }\n }\n return undefined\n}\n"],"names":["sources","Map","listeners","Set","version","snapshot","notify","values","listener","registerRepeatSource","source","get","id","set","delete","listRepeatSources","getRepeatSource","subscribeRepeatSources","add","getRepeatSourcesVersion","repeatSourceOf","node","props","undefined","value","keyProp","key","trim"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GA4ED,MAAMA,UAAU,IAAIC;AACpB,MAAMC,YAAY,IAAIC;AACtB,IAAIC,UAAU;AACd;;;;CAIC,GACD,IAAIC,WAAoC,EAAE;AAE1C,MAAMC,SAAS;IACbF,WAAW;IACXC,WAAW;WAAIL,QAAQO,MAAM;KAAG;IAChC,KAAK,MAAMC,YAAY;WAAIN;KAAU,CAAEM;AACzC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,qBAAqBC,MAAoB;IACvD,IAAIV,QAAQW,GAAG,CAACD,OAAOE,EAAE,MAAMF,QAAQ;QACrCV,QAAQa,GAAG,CAACH,OAAOE,EAAE,EAAEF;QACvBJ;IACF;IACA,OAAO;QACL,IAAIN,QAAQW,GAAG,CAACD,OAAOE,EAAE,MAAMF,QAAQ;QACvCV,QAAQc,MAAM,CAACJ,OAAOE,EAAE;QACxBN;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,SAASS;IACd,OAAOV;AACT;AAEA,sCAAsC,GACtC,OAAO,SAASW,gBAAgBJ,EAAU;IACxC,OAAOZ,QAAQW,GAAG,CAACC;AACrB;AAEA,sEAAsE,GACtE,OAAO,SAASK,uBAAuBT,QAAoB;IACzDN,UAAUgB,GAAG,CAACV;IACd,OAAO;QACLN,UAAUY,MAAM,CAACN;IACnB;AACF;AAEA,uEAAuE,GACvE,OAAO,SAASW;IACd,OAAOf;AACT;AAEA;;;CAGC,GACD,OAAO,SAASgB,eACdC,IAAa;IAEb,MAAMC,QAASD,wBAAD,AAACA,KAA0DC,KAAK;IAC9E,IAAI,CAACA,OAAO,OAAOC;IACnB,KAAK,MAAMb,UAAUV,QAAQO,MAAM,GAAI;QACrC,MAAMiB,QAAQF,KAAK,CAACZ,OAAOe,OAAO,CAAC;QACnC,MAAMC,MAAM,OAAOF,UAAU,WAAWA,MAAMG,IAAI,KAAK;QACvD,IAAID,KAAK,OAAO;YAAEhB;YAAQgB;QAAI;IAChC;IACA,OAAOH;AACT"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/repeat-sources.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AglynAttributeSchema } from '../foundation/definitions/components.types'\nimport type { EntityPickerKind } from './entity-picker-context'\nimport type { RepeatableDataset } from './expand-repeatables'\n\n/**\n * Where a repeat's rows come from, for the editor (AGL-3111).\n *\n * Any element can repeat, and the platform decides what a repeat MEANS — the\n * scope, the copies, the bounds (`expand-repeatables.ts`). What it does not\n * decide is what an element repeats OVER. That belongs to the plugin that owns\n * the rows, which registers a source here from the registration function its\n * loader calls by name. Core names no source.\n *\n * Reached by path, never through a barrel: nothing on a published page asks\n * where rows come from, so nothing here is in a published page's download.\n */\n\n/**\n * What a source answers for one key.\n *\n * - `loading` — no answer yet.\n * - `missing` — the key names nothing this site may use. The published page\n * renders the element once, as written.\n * - `error` — the read failed. Nothing is known about the rows.\n * - `ready` — the rows, read and bounded exactly as the published page's\n * composition reads them.\n */\nexport type RepeatRowsAnswer =\n | { status: 'loading' }\n | { status: 'missing' }\n | { status: 'error' }\n | {\n status: 'ready'\n /** The name authors know the entry by, for the canvas badge. */\n label: string\n /**\n * The rows under every key the expansion looks them up by: the key as\n * the node stores it, the entry's id, and each entry a one-hop reference\n * in the rows reads (`{{item.ref.field}}`).\n */\n rowsByKey: Readonly<Record<string, RepeatableDataset>>\n }\n\n/** One read a source is asked for. */\nexport interface RepeatRowsRequest {\n /** The site the canvas edits. A source answers only what it may show. */\n hostId: string\n /** The key exactly as the node stores it. */\n key: string\n}\n\n/** One kind of thing an element can repeat over. */\nexport interface RepeatSource {\n /** Stable id, e.g. the plugin's name for the kind. */\n id: string\n /** What the Attributes panel calls the kind. */\n label: string\n /**\n * The node prop the chosen key persists under. Persisted in documents, so a\n * source never renames it.\n */\n keyProp: string\n /**\n * The attribute that picks a key, drawn by the Attributes panel as any\n * component attribute is. The panel names the field after `keyProp`.\n */\n keyAttribute: Omit<AglynAttributeSchema, 'name'>\n /**\n * The entity picker kind a key names, when a key is an entity the picker\n * lists. With it, the insert-token menu inside the repeat names the entity\n * through the picker and offers `{{item.<field>}}` for each field the\n * entity declares (`EntityPickerContextValue.entityFields`). Without it the\n * menu offers no item tokens and authors type them.\n */\n entityKind?: EntityPickerKind\n /**\n * Reads one key's rows for the besigner's canvas preview and badge. A React\n * hook: the editor mounts one reader per key and calls it unconditionally,\n * so its identity must not change after registration.\n */\n useRows(request: RepeatRowsRequest): RepeatRowsAnswer\n}\n\nconst sources = new Map<string, RepeatSource>()\nconst listeners = new Set<() => void>()\nlet version = 0\n/**\n * Replaced only when the registered set changes, never rebuilt per call: it is\n * the snapshot `useSyncExternalStore` compares, and a fresh array every time\n * is a render loop rather than a wasted allocation.\n */\nlet snapshot: readonly RepeatSource[] = []\n\nconst notify = () => {\n version += 1\n snapshot = [...sources.values()]\n for (const listener of [...listeners]) listener()\n}\n\n/**\n * Registers a source, replacing any registered under its id. Returns the\n * unregister, which removes the source only if it is still the one registered.\n *\n * Call it from a registration function the plugin loader calls BY NAME, never\n * from a module's top level: a bundler deletes a module imported only for its\n * side effect when its package says it has none (AGL-3025).\n */\nexport function registerRepeatSource(source: RepeatSource): () => void {\n if (sources.get(source.id) !== source) {\n sources.set(source.id, source)\n notify()\n }\n return () => {\n if (sources.get(source.id) !== source) return\n sources.delete(source.id)\n notify()\n }\n}\n\n/**\n * Every registered source, in registration order. The SAME array until the\n * registered set changes, so it can be read as a store snapshot.\n */\nexport function listRepeatSources(): readonly RepeatSource[] {\n return snapshot\n}\n\n/** The source registered under `id`. */\nexport function getRepeatSource(id: string): RepeatSource | undefined {\n return sources.get(id)\n}\n\n/** Called after the registered set changes. Returns the unsubscribe. */\nexport function subscribeRepeatSources(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** Moves on every change — the snapshot `useSyncExternalStore` reads. */\nexport function getRepeatSourcesVersion(): number {\n return version\n}\n\n/**\n * The registered source a node repeats over, and the key it names, trimmed.\n * `undefined` for a node that repeats over nothing a registered source owns.\n */\nexport function repeatSourceOf(\n node: unknown,\n): { source: RepeatSource; key: string } | undefined {\n const props = (node as { props?: Record<string, unknown> } | undefined)?.props\n if (!props) return undefined\n for (const source of sources.values()) {\n const value = props[source.keyProp]\n const key = typeof value === 'string' ? value.trim() : ''\n if (key) return { source, key }\n }\n return undefined\n}\n"],"names":["sources","Map","listeners","Set","version","snapshot","notify","values","listener","registerRepeatSource","source","get","id","set","delete","listRepeatSources","getRepeatSource","subscribeRepeatSources","add","getRepeatSourcesVersion","repeatSourceOf","node","props","undefined","value","keyProp","key","trim"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAqFD,MAAMA,UAAU,IAAIC;AACpB,MAAMC,YAAY,IAAIC;AACtB,IAAIC,UAAU;AACd;;;;CAIC,GACD,IAAIC,WAAoC,EAAE;AAE1C,MAAMC,SAAS;IACbF,WAAW;IACXC,WAAW;WAAIL,QAAQO,MAAM;KAAG;IAChC,KAAK,MAAMC,YAAY;WAAIN;KAAU,CAAEM;AACzC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,qBAAqBC,MAAoB;IACvD,IAAIV,QAAQW,GAAG,CAACD,OAAOE,EAAE,MAAMF,QAAQ;QACrCV,QAAQa,GAAG,CAACH,OAAOE,EAAE,EAAEF;QACvBJ;IACF;IACA,OAAO;QACL,IAAIN,QAAQW,GAAG,CAACD,OAAOE,EAAE,MAAMF,QAAQ;QACvCV,QAAQc,MAAM,CAACJ,OAAOE,EAAE;QACxBN;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,SAASS;IACd,OAAOV;AACT;AAEA,sCAAsC,GACtC,OAAO,SAASW,gBAAgBJ,EAAU;IACxC,OAAOZ,QAAQW,GAAG,CAACC;AACrB;AAEA,sEAAsE,GACtE,OAAO,SAASK,uBAAuBT,QAAoB;IACzDN,UAAUgB,GAAG,CAACV;IACd,OAAO;QACLN,UAAUY,MAAM,CAACN;IACnB;AACF;AAEA,uEAAuE,GACvE,OAAO,SAASW;IACd,OAAOf;AACT;AAEA;;;CAGC,GACD,OAAO,SAASgB,eACdC,IAAa;IAEb,MAAMC,QAASD,wBAAD,AAACA,KAA0DC,KAAK;IAC9E,IAAI,CAACA,OAAO,OAAOC;IACnB,KAAK,MAAMb,UAAUV,QAAQO,MAAM,GAAI;QACrC,MAAMiB,QAAQF,KAAK,CAACZ,OAAOe,OAAO,CAAC;QACnC,MAAMC,MAAM,OAAOF,UAAU,WAAWA,MAAMG,IAAI,KAAK;QACvD,IAAID,KAAK,OAAO;YAAEhB;YAAQgB;QAAI;IAChC;IACA,OAAOH;AACT"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /** `orgs/{orgId}/retainedRefusals/{personKey}` — server-only, closed to every client. */
18
+ export declare const RETAINED_REFUSALS_COLLECTION = "retainedRefusals";
19
+ /** When a refusal was retained, epoch ms: on each entry and on the document. */
20
+ export declare const RETAINED_AT_FIELD = "retainedAtMs";
21
+ /** The record an entry was retained from, on each entry. */
22
+ export declare const RETAINED_FROM_RECORD_FIELD = "retainedFromContactId";
23
+ /** Every refusal one person's record holds. */
24
+ export interface RetainedRefusals {
25
+ /** Each site's refusal entry, keyed by the site, as the record stored it. */
26
+ byHost: Record<string, Record<string, unknown>>;
27
+ /** The top-level refusal, which names no site and is honored against every one. */
28
+ unscoped: boolean;
29
+ }
30
+ type Doc = Record<string, unknown>;
31
+ /**
32
+ * Every refusal `record` holds, or `null` when it holds none.
33
+ *
34
+ * Every SITE, not the sites of one group: a record can carry a refusal for a
35
+ * site that let it go earlier, or one a consent group change carried onto
36
+ * it, and each is still somebody's "no". Reads a retained document as well
37
+ * as a person record, since the two share their field names.
38
+ */
39
+ export declare function refusalsOf(record: Doc | null | undefined): RetainedRefusals | null;
40
+ /**
41
+ * `record` with what was retained for the same person laid over it — the
42
+ * record a reader of the stored basis should read.
43
+ *
44
+ * Per retained site, the record's entry stands only when it is a GRANT whose
45
+ * date is strictly after the refusal's (the person's own date, or when it was
46
+ * retained where they gave none). Anything else — no entry, a refusal of its
47
+ * own, an older or undated grant — reads as the retained refusal. The
48
+ * unscoped refusal is laid on only where the record gives no top-level
49
+ * answer of its own.
50
+ *
51
+ * With nothing retained the record comes back as it was, `null` included, so
52
+ * a person with no retained refusal reads exactly as they did before this
53
+ * store existed.
54
+ */
55
+ export declare function withRetainedRefusals(record: Doc | null | undefined, retained: RetainedRefusals | null | undefined): Doc | null;
56
+ /**
57
+ * The entries one retention writes: each refusal stamped with when it was
58
+ * retained and from which record, keyed by site.
59
+ */
60
+ export declare function retainedEntries(retained: RetainedRefusals, recordId: string, nowMs: number): Record<string, Doc>;
61
+ /**
62
+ * One retained document's share of a consent group change's carry `to ← from`
63
+ * — the dotted-path patch that gives `to` the refusal `from` holds, or `null`
64
+ * when `from` holds none or `to` already has an entry.
65
+ *
66
+ * Never overwrites: every entry in this store is a refusal, so an entry on
67
+ * `to` is already the answer, and a re-run after a crash writes nothing.
68
+ */
69
+ export declare function retainedRefusalCarry(retained: Doc | null | undefined, carry: {
70
+ toHostId: string;
71
+ fromHostId: string;
72
+ }, changeId: string): Doc | null;
73
+ export {};
@@ -0,0 +1,186 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ /**
18
+ * RETAINED REFUSALS — a person's "no" outlives the record it was written on
19
+ * (AGL-3338).
20
+ *
21
+ * ## Where a refusal lives, and why that is not enough
22
+ *
23
+ * A person's refusal of marketing email is stored on the record an operator
24
+ * keeps about them: an entry under {@link MARKETING_CONSENT_BY_HOST_FIELD}
25
+ * for the site they refused, or the top-level field for a refusal that names
26
+ * no site. The list gate reads it there. An operator adding people to a list
27
+ * by hand or by import states that they have permission to email them, and a
28
+ * stored refusal is the one fact that attestation cannot overrule: the
29
+ * person said no, and no operator's word turns that into a yes.
30
+ *
31
+ * The record, though, is the operator's, and the operator may delete it. A
32
+ * deleted record is an address with no record at all, which reads as
33
+ * `unrecorded` — nothing known either way — and the attestation is accepted.
34
+ * Deleting somebody from a CRM would then be the way to mail a person who
35
+ * refused, which is the one outcome the refusal exists to prevent.
36
+ *
37
+ * So a refusal is not the record's to lose. When a holder lets a shared
38
+ * record go, its refusal entries stay on the document for the holders that
39
+ * remain. When the last holder lets go, every refusal the document holds —
40
+ * every site's, not only the deleting holder's, and the unscoped one — is
41
+ * copied into {@link RETAINED_REFUSALS_COLLECTION} in the same transaction
42
+ * that deletes it, and the list gate reads that store beside the records.
43
+ *
44
+ * ## No address
45
+ *
46
+ * One document per person, keyed by `personKey` — the sha256 of the
47
+ * normalized address — and holding no address, as a suppression row keyed
48
+ * the same way holds none. The store answers one question about an address
49
+ * somebody already has in hand, "did this person refuse", and it has no
50
+ * business being a list of the people who did.
51
+ *
52
+ * ## The same field names as the record
53
+ *
54
+ * An entry keeps the record's own names, so a record merged with what was
55
+ * retained ({@link withRetainedRefusals}) is read by `readMarketingBasis`
56
+ * like any other — a second set of names would be a second parser for the
57
+ * two to disagree through. Each entry adds when it was retained and from
58
+ * which record.
59
+ *
60
+ * ## A newer grant wins, and nothing else does
61
+ *
62
+ * The person may come back. Signing up again on a form that asks writes a
63
+ * grant, and a grant given after the refusal is the person changing their
64
+ * mind — the merge lets it stand. A grant that is not strictly newer, a grant
65
+ * with no date, and every entry that is not a grant read as the refusal
66
+ * still: withholding mail is recoverable, and sending it is not.
67
+ *
68
+ * ## Carried like the refusals it came from
69
+ *
70
+ * The list gate reads a refusal across the CURRENT consent group, so two
71
+ * sites separating would stop one seeing the other's retained refusals. A
72
+ * consent group change therefore carries this store as it carries the site
73
+ * suppression lists and a record's own refusals: {@link retainedRefusalCarry}
74
+ * decides one document, and the executor pages the store.
75
+ */ import { MARKETING_CONSENT_BY_HOST_FIELD, MARKETING_CONSENT_FIELD } from "./marketing-consent.js";
76
+ /** `orgs/{orgId}/retainedRefusals/{personKey}` — server-only, closed to every client. */ export const RETAINED_REFUSALS_COLLECTION = 'retainedRefusals';
77
+ /** When a refusal was retained, epoch ms: on each entry and on the document. */ export const RETAINED_AT_FIELD = 'retainedAtMs';
78
+ /** The record an entry was retained from, on each entry. */ export const RETAINED_FROM_RECORD_FIELD = 'retainedFromContactId';
79
+ /** When the person gave the answer an entry records, as every writer stamps it. */ const GIVEN_AT_FIELD = 'marketingConsentAtMs';
80
+ function mapOf(value) {
81
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
82
+ }
83
+ /** Epoch millis off a number, a `Date` or either SDK's `Timestamp`, else `null`. */ function millisOf(value) {
84
+ if (typeof value === 'number') return Number.isFinite(value) ? value : null;
85
+ if (value instanceof Date) return value.getTime();
86
+ const toMillis = value == null ? void 0 : value.toMillis;
87
+ if (typeof toMillis === 'function') {
88
+ const millis = Number(toMillis.call(value));
89
+ return Number.isFinite(millis) ? millis : null;
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Every refusal `record` holds, or `null` when it holds none.
95
+ *
96
+ * Every SITE, not the sites of one group: a record can carry a refusal for a
97
+ * site that let it go earlier, or one a consent group change carried onto
98
+ * it, and each is still somebody's "no". Reads a retained document as well
99
+ * as a person record, since the two share their field names.
100
+ */ export function refusalsOf(record) {
101
+ var _mapOf;
102
+ if (!record) return null;
103
+ const byHost = {};
104
+ for (const [hostId, value] of Object.entries((_mapOf = mapOf(record[MARKETING_CONSENT_BY_HOST_FIELD])) != null ? _mapOf : {})){
105
+ const entry = mapOf(value);
106
+ if (hostId && (entry == null ? void 0 : entry[MARKETING_CONSENT_FIELD]) === false) byHost[hostId] = _extends({}, entry);
107
+ }
108
+ const unscoped = record[MARKETING_CONSENT_FIELD] === false;
109
+ return unscoped || Object.keys(byHost).length ? {
110
+ byHost,
111
+ unscoped
112
+ } : null;
113
+ }
114
+ /** When a retained refusal counts as given: the person's own date, else the retention's. */ function refusedAtMs(refusal) {
115
+ var _millisOf;
116
+ return (_millisOf = millisOf(refusal[GIVEN_AT_FIELD])) != null ? _millisOf : millisOf(refusal[RETAINED_AT_FIELD]);
117
+ }
118
+ /**
119
+ * `record` with what was retained for the same person laid over it — the
120
+ * record a reader of the stored basis should read.
121
+ *
122
+ * Per retained site, the record's entry stands only when it is a GRANT whose
123
+ * date is strictly after the refusal's (the person's own date, or when it was
124
+ * retained where they gave none). Anything else — no entry, a refusal of its
125
+ * own, an older or undated grant — reads as the retained refusal. The
126
+ * unscoped refusal is laid on only where the record gives no top-level
127
+ * answer of its own.
128
+ *
129
+ * With nothing retained the record comes back as it was, `null` included, so
130
+ * a person with no retained refusal reads exactly as they did before this
131
+ * store existed.
132
+ */ export function withRetainedRefusals(record, retained) {
133
+ var _ref, _mapOf;
134
+ const hosts = Object.entries((_ref = retained == null ? void 0 : retained.byHost) != null ? _ref : {});
135
+ if (!retained || !hosts.length && !retained.unscoped) return record != null ? record : null;
136
+ const base = record != null ? record : {};
137
+ const byHost = _extends({}, (_mapOf = mapOf(base[MARKETING_CONSENT_BY_HOST_FIELD])) != null ? _mapOf : {});
138
+ for (const [hostId, refusal] of hosts){
139
+ const entry = mapOf(byHost[hostId]);
140
+ const grantedAt = (entry == null ? void 0 : entry[MARKETING_CONSENT_FIELD]) === true ? millisOf(entry[GIVEN_AT_FIELD]) : null;
141
+ const refusedAt = refusedAtMs(refusal);
142
+ const newerGrant = grantedAt !== null && refusedAt !== null && grantedAt > refusedAt;
143
+ if (!newerGrant) byHost[hostId] = refusal;
144
+ }
145
+ const merged = _extends({}, base, {
146
+ [MARKETING_CONSENT_BY_HOST_FIELD]: byHost
147
+ });
148
+ if (retained.unscoped && typeof base[MARKETING_CONSENT_FIELD] !== 'boolean') {
149
+ merged[MARKETING_CONSENT_FIELD] = false;
150
+ }
151
+ return merged;
152
+ }
153
+ /**
154
+ * The entries one retention writes: each refusal stamped with when it was
155
+ * retained and from which record, keyed by site.
156
+ */ export function retainedEntries(retained, recordId, nowMs) {
157
+ return Object.fromEntries(Object.entries(retained.byHost).map(([hostId, refusal])=>[
158
+ hostId,
159
+ _extends({}, refusal, {
160
+ [RETAINED_AT_FIELD]: nowMs,
161
+ [RETAINED_FROM_RECORD_FIELD]: recordId
162
+ })
163
+ ]));
164
+ }
165
+ /**
166
+ * One retained document's share of a consent group change's carry `to ← from`
167
+ * — the dotted-path patch that gives `to` the refusal `from` holds, or `null`
168
+ * when `from` holds none or `to` already has an entry.
169
+ *
170
+ * Never overwrites: every entry in this store is a refusal, so an entry on
171
+ * `to` is already the answer, and a re-run after a crash writes nothing.
172
+ */ export function retainedRefusalCarry(retained, carry, changeId) {
173
+ var _mapOf;
174
+ const byHost = (_mapOf = mapOf(retained == null ? void 0 : retained[MARKETING_CONSENT_BY_HOST_FIELD])) != null ? _mapOf : {};
175
+ const source = mapOf(byHost[carry.fromHostId]);
176
+ if ((source == null ? void 0 : source[MARKETING_CONSENT_FIELD]) !== false) return null;
177
+ if (byHost[carry.toHostId] !== undefined) return null;
178
+ return {
179
+ [`${MARKETING_CONSENT_BY_HOST_FIELD}.${carry.toHostId}`]: _extends({}, source, {
180
+ carriedFromHostId: carry.fromHostId,
181
+ carriedByChangeId: changeId
182
+ })
183
+ };
184
+ }
185
+
186
+ //# sourceMappingURL=retained-refusals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/retained-refusals.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * RETAINED REFUSALS — a person's \"no\" outlives the record it was written on\n * (AGL-3338).\n *\n * ## Where a refusal lives, and why that is not enough\n *\n * A person's refusal of marketing email is stored on the record an operator\n * keeps about them: an entry under {@link MARKETING_CONSENT_BY_HOST_FIELD}\n * for the site they refused, or the top-level field for a refusal that names\n * no site. The list gate reads it there. An operator adding people to a list\n * by hand or by import states that they have permission to email them, and a\n * stored refusal is the one fact that attestation cannot overrule: the\n * person said no, and no operator's word turns that into a yes.\n *\n * The record, though, is the operator's, and the operator may delete it. A\n * deleted record is an address with no record at all, which reads as\n * `unrecorded` — nothing known either way — and the attestation is accepted.\n * Deleting somebody from a CRM would then be the way to mail a person who\n * refused, which is the one outcome the refusal exists to prevent.\n *\n * So a refusal is not the record's to lose. When a holder lets a shared\n * record go, its refusal entries stay on the document for the holders that\n * remain. When the last holder lets go, every refusal the document holds —\n * every site's, not only the deleting holder's, and the unscoped one — is\n * copied into {@link RETAINED_REFUSALS_COLLECTION} in the same transaction\n * that deletes it, and the list gate reads that store beside the records.\n *\n * ## No address\n *\n * One document per person, keyed by `personKey` — the sha256 of the\n * normalized address — and holding no address, as a suppression row keyed\n * the same way holds none. The store answers one question about an address\n * somebody already has in hand, \"did this person refuse\", and it has no\n * business being a list of the people who did.\n *\n * ## The same field names as the record\n *\n * An entry keeps the record's own names, so a record merged with what was\n * retained ({@link withRetainedRefusals}) is read by `readMarketingBasis`\n * like any other — a second set of names would be a second parser for the\n * two to disagree through. Each entry adds when it was retained and from\n * which record.\n *\n * ## A newer grant wins, and nothing else does\n *\n * The person may come back. Signing up again on a form that asks writes a\n * grant, and a grant given after the refusal is the person changing their\n * mind — the merge lets it stand. A grant that is not strictly newer, a grant\n * with no date, and every entry that is not a grant read as the refusal\n * still: withholding mail is recoverable, and sending it is not.\n *\n * ## Carried like the refusals it came from\n *\n * The list gate reads a refusal across the CURRENT consent group, so two\n * sites separating would stop one seeing the other's retained refusals. A\n * consent group change therefore carries this store as it carries the site\n * suppression lists and a record's own refusals: {@link retainedRefusalCarry}\n * decides one document, and the executor pages the store.\n */\n\nimport {\n MARKETING_CONSENT_BY_HOST_FIELD,\n MARKETING_CONSENT_FIELD,\n} from './marketing-consent'\n\n/** `orgs/{orgId}/retainedRefusals/{personKey}` — server-only, closed to every client. */\nexport const RETAINED_REFUSALS_COLLECTION = 'retainedRefusals'\n\n/** When a refusal was retained, epoch ms: on each entry and on the document. */\nexport const RETAINED_AT_FIELD = 'retainedAtMs'\n\n/** The record an entry was retained from, on each entry. */\nexport const RETAINED_FROM_RECORD_FIELD = 'retainedFromContactId'\n\n/** When the person gave the answer an entry records, as every writer stamps it. */\nconst GIVEN_AT_FIELD = 'marketingConsentAtMs'\n\n/** Every refusal one person's record holds. */\nexport interface RetainedRefusals {\n /** Each site's refusal entry, keyed by the site, as the record stored it. */\n byHost: Record<string, Record<string, unknown>>\n /** The top-level refusal, which names no site and is honored against every one. */\n unscoped: boolean\n}\n\ntype Doc = Record<string, unknown>\n\nfunction mapOf(value: unknown): Doc | null {\n return value && typeof value === 'object' && !Array.isArray(value) ? (value as Doc) : null\n}\n\n/** Epoch millis off a number, a `Date` or either SDK's `Timestamp`, else `null`. */\nfunction millisOf(value: unknown): number | null {\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (value instanceof Date) return value.getTime()\n const toMillis = (value as { toMillis?: unknown } | null)?.toMillis\n if (typeof toMillis === 'function') {\n const millis = Number(toMillis.call(value))\n return Number.isFinite(millis) ? millis : null\n }\n return null\n}\n\n/**\n * Every refusal `record` holds, or `null` when it holds none.\n *\n * Every SITE, not the sites of one group: a record can carry a refusal for a\n * site that let it go earlier, or one a consent group change carried onto\n * it, and each is still somebody's \"no\". Reads a retained document as well\n * as a person record, since the two share their field names.\n */\nexport function refusalsOf(record: Doc | null | undefined): RetainedRefusals | null {\n if (!record) return null\n const byHost: Record<string, Doc> = {}\n for (const [hostId, value] of Object.entries(mapOf(record[MARKETING_CONSENT_BY_HOST_FIELD]) ?? {})) {\n const entry = mapOf(value)\n if (hostId && entry?.[MARKETING_CONSENT_FIELD] === false) byHost[hostId] = { ...entry }\n }\n const unscoped = record[MARKETING_CONSENT_FIELD] === false\n return unscoped || Object.keys(byHost).length ? { byHost, unscoped } : null\n}\n\n/** When a retained refusal counts as given: the person's own date, else the retention's. */\nfunction refusedAtMs(refusal: Doc): number | null {\n return millisOf(refusal[GIVEN_AT_FIELD]) ?? millisOf(refusal[RETAINED_AT_FIELD])\n}\n\n/**\n * `record` with what was retained for the same person laid over it — the\n * record a reader of the stored basis should read.\n *\n * Per retained site, the record's entry stands only when it is a GRANT whose\n * date is strictly after the refusal's (the person's own date, or when it was\n * retained where they gave none). Anything else — no entry, a refusal of its\n * own, an older or undated grant — reads as the retained refusal. The\n * unscoped refusal is laid on only where the record gives no top-level\n * answer of its own.\n *\n * With nothing retained the record comes back as it was, `null` included, so\n * a person with no retained refusal reads exactly as they did before this\n * store existed.\n */\nexport function withRetainedRefusals(\n record: Doc | null | undefined,\n retained: RetainedRefusals | null | undefined,\n): Doc | null {\n const hosts = Object.entries(retained?.byHost ?? {})\n if (!retained || (!hosts.length && !retained.unscoped)) return record ?? null\n const base = record ?? {}\n const byHost: Doc = { ...(mapOf(base[MARKETING_CONSENT_BY_HOST_FIELD]) ?? {}) }\n for (const [hostId, refusal] of hosts) {\n const entry = mapOf(byHost[hostId])\n const grantedAt = entry?.[MARKETING_CONSENT_FIELD] === true ? millisOf(entry[GIVEN_AT_FIELD]) : null\n const refusedAt = refusedAtMs(refusal)\n const newerGrant = grantedAt !== null && refusedAt !== null && grantedAt > refusedAt\n if (!newerGrant) byHost[hostId] = refusal\n }\n const merged: Doc = { ...base, [MARKETING_CONSENT_BY_HOST_FIELD]: byHost }\n if (retained.unscoped && typeof base[MARKETING_CONSENT_FIELD] !== 'boolean') {\n merged[MARKETING_CONSENT_FIELD] = false\n }\n return merged\n}\n\n/**\n * The entries one retention writes: each refusal stamped with when it was\n * retained and from which record, keyed by site.\n */\nexport function retainedEntries(\n retained: RetainedRefusals,\n recordId: string,\n nowMs: number,\n): Record<string, Doc> {\n return Object.fromEntries(\n Object.entries(retained.byHost).map(([hostId, refusal]) => [\n hostId,\n { ...refusal, [RETAINED_AT_FIELD]: nowMs, [RETAINED_FROM_RECORD_FIELD]: recordId },\n ]),\n )\n}\n\n/**\n * One retained document's share of a consent group change's carry `to ← from`\n * — the dotted-path patch that gives `to` the refusal `from` holds, or `null`\n * when `from` holds none or `to` already has an entry.\n *\n * Never overwrites: every entry in this store is a refusal, so an entry on\n * `to` is already the answer, and a re-run after a crash writes nothing.\n */\nexport function retainedRefusalCarry(\n retained: Doc | null | undefined,\n carry: { toHostId: string; fromHostId: string },\n changeId: string,\n): Doc | null {\n const byHost = mapOf(retained?.[MARKETING_CONSENT_BY_HOST_FIELD]) ?? {}\n const source = mapOf(byHost[carry.fromHostId])\n if (source?.[MARKETING_CONSENT_FIELD] !== false) return null\n if (byHost[carry.toHostId] !== undefined) return null\n return {\n [`${MARKETING_CONSENT_BY_HOST_FIELD}.${carry.toHostId}`]: {\n ...source,\n carriedFromHostId: carry.fromHostId,\n carriedByChangeId: changeId,\n },\n }\n}\n"],"names":["MARKETING_CONSENT_BY_HOST_FIELD","MARKETING_CONSENT_FIELD","RETAINED_REFUSALS_COLLECTION","RETAINED_AT_FIELD","RETAINED_FROM_RECORD_FIELD","GIVEN_AT_FIELD","mapOf","value","Array","isArray","millisOf","Number","isFinite","Date","getTime","toMillis","millis","call","refusalsOf","record","byHost","hostId","Object","entries","entry","unscoped","keys","length","refusedAtMs","refusal","withRetainedRefusals","retained","hosts","base","grantedAt","refusedAt","newerGrant","merged","retainedEntries","recordId","nowMs","fromEntries","map","retainedRefusalCarry","carry","changeId","source","fromHostId","toHostId","undefined","carriedFromHostId","carriedByChangeId"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DC,GAED,SACEA,+BAA+B,EAC/BC,uBAAuB,QAClB,yBAAqB;AAE5B,uFAAuF,GACvF,OAAO,MAAMC,+BAA+B,mBAAkB;AAE9D,8EAA8E,GAC9E,OAAO,MAAMC,oBAAoB,eAAc;AAE/C,0DAA0D,GAC1D,OAAO,MAAMC,6BAA6B,wBAAuB;AAEjE,iFAAiF,GACjF,MAAMC,iBAAiB;AAYvB,SAASC,MAAMC,KAAc;IAC3B,OAAOA,SAAS,OAAOA,UAAU,YAAY,CAACC,MAAMC,OAAO,CAACF,SAAUA,QAAgB;AACxF;AAEA,kFAAkF,GAClF,SAASG,SAASH,KAAc;IAC9B,IAAI,OAAOA,UAAU,UAAU,OAAOI,OAAOC,QAAQ,CAACL,SAASA,QAAQ;IACvE,IAAIA,iBAAiBM,MAAM,OAAON,MAAMO,OAAO;IAC/C,MAAMC,WAAYR,yBAAD,AAACA,MAAyCQ,QAAQ;IACnE,IAAI,OAAOA,aAAa,YAAY;QAClC,MAAMC,SAASL,OAAOI,SAASE,IAAI,CAACV;QACpC,OAAOI,OAAOC,QAAQ,CAACI,UAAUA,SAAS;IAC5C;IACA,OAAO;AACT;AAEA;;;;;;;CAOC,GACD,OAAO,SAASE,WAAWC,MAA8B;QAGVb;IAF7C,IAAI,CAACa,QAAQ,OAAO;IACpB,MAAMC,SAA8B,CAAC;IACrC,KAAK,MAAM,CAACC,QAAQd,MAAM,IAAIe,OAAOC,OAAO,EAACjB,SAAAA,MAAMa,MAAM,CAACnB,gCAAgC,aAA7CM,SAAkD,CAAC,GAAI;QAClG,MAAMkB,QAAQlB,MAAMC;QACpB,IAAIc,UAAUG,CAAAA,yBAAAA,KAAO,CAACvB,wBAAwB,MAAK,OAAOmB,MAAM,CAACC,OAAO,GAAG,aAAKG;IAClF;IACA,MAAMC,WAAWN,MAAM,CAAClB,wBAAwB,KAAK;IACrD,OAAOwB,YAAYH,OAAOI,IAAI,CAACN,QAAQO,MAAM,GAAG;QAAEP;QAAQK;IAAS,IAAI;AACzE;AAEA,0FAA0F,GAC1F,SAASG,YAAYC,OAAY;QACxBnB;IAAP,QAAOA,YAAAA,SAASmB,OAAO,CAACxB,eAAe,aAAhCK,YAAqCA,SAASmB,OAAO,CAAC1B,kBAAkB;AACjF;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAAS2B,qBACdX,MAA8B,EAC9BY,QAA6C;cAKnBzB;IAH1B,MAAM0B,QAAQV,OAAOC,OAAO,SAACQ,4BAAAA,SAAUX,MAAM,mBAAI,CAAC;IAClD,IAAI,CAACW,YAAa,CAACC,MAAML,MAAM,IAAI,CAACI,SAASN,QAAQ,EAAG,OAAON,iBAAAA,SAAU;IACzE,MAAMc,OAAOd,iBAAAA,SAAU,CAAC;IACxB,MAAMC,SAAc,cAAMd,SAAAA,MAAM2B,IAAI,CAACjC,gCAAgC,aAA3CM,SAAgD,CAAC;IAC3E,KAAK,MAAM,CAACe,QAAQQ,QAAQ,IAAIG,MAAO;QACrC,MAAMR,QAAQlB,MAAMc,MAAM,CAACC,OAAO;QAClC,MAAMa,YAAYV,CAAAA,yBAAAA,KAAO,CAACvB,wBAAwB,MAAK,OAAOS,SAASc,KAAK,CAACnB,eAAe,IAAI;QAChG,MAAM8B,YAAYP,YAAYC;QAC9B,MAAMO,aAAaF,cAAc,QAAQC,cAAc,QAAQD,YAAYC;QAC3E,IAAI,CAACC,YAAYhB,MAAM,CAACC,OAAO,GAAGQ;IACpC;IACA,MAAMQ,SAAc,aAAKJ;QAAM,CAACjC,gCAAgC,EAAEoB;;IAClE,IAAIW,SAASN,QAAQ,IAAI,OAAOQ,IAAI,CAAChC,wBAAwB,KAAK,WAAW;QAC3EoC,MAAM,CAACpC,wBAAwB,GAAG;IACpC;IACA,OAAOoC;AACT;AAEA;;;CAGC,GACD,OAAO,SAASC,gBACdP,QAA0B,EAC1BQ,QAAgB,EAChBC,KAAa;IAEb,OAAOlB,OAAOmB,WAAW,CACvBnB,OAAOC,OAAO,CAACQ,SAASX,MAAM,EAAEsB,GAAG,CAAC,CAAC,CAACrB,QAAQQ,QAAQ,GAAK;YACzDR;YACA,aAAKQ;gBAAS,CAAC1B,kBAAkB,EAAEqC;gBAAO,CAACpC,2BAA2B,EAAEmC;;SACzE;AAEL;AAEA;;;;;;;CAOC,GACD,OAAO,SAASI,qBACdZ,QAAgC,EAChCa,KAA+C,EAC/CC,QAAgB;QAEDvC;IAAf,MAAMc,UAASd,SAAAA,MAAMyB,4BAAAA,QAAU,CAAC/B,gCAAgC,aAAjDM,SAAsD,CAAC;IACtE,MAAMwC,SAASxC,MAAMc,MAAM,CAACwB,MAAMG,UAAU,CAAC;IAC7C,IAAID,CAAAA,0BAAAA,MAAQ,CAAC7C,wBAAwB,MAAK,OAAO,OAAO;IACxD,IAAImB,MAAM,CAACwB,MAAMI,QAAQ,CAAC,KAAKC,WAAW,OAAO;IACjD,OAAO;QACL,CAAC,GAAGjD,gCAAgC,CAAC,EAAE4C,MAAMI,QAAQ,EAAE,CAAC,EAAE,aACrDF;YACHI,mBAAmBN,MAAMG,UAAU;YACnCI,mBAAmBN;;IAEvB;AACF"}
@@ -247,3 +247,30 @@ export declare function scopeCovers(target: readonly string[] | undefined | null
247
247
  * org CDN URL. That keeps it safe to apply blindly at every pick site.
248
248
  */
249
249
  export declare function hostQualifiedCdnPath(cdnPath: string | undefined | null, visibleTo: readonly string[] | undefined | null, hostId: string | undefined | null): string | undefined;
250
+ /** The map a scoped document records its visibility grants under. */
251
+ export declare const SCOPE_GRANTS_FIELD = "sharing";
252
+ /** The tokens a grant ADDED to `visibleTo`: seen through, never held. */
253
+ export declare function grantedScopeTokens(doc: Readonly<Record<string, unknown>> | null | undefined): string[];
254
+ /**
255
+ * The tokens that HOLD a document: `visibleTo` without the ones a grant
256
+ * added. Missing `visibleTo` is held by nobody, as it is seen by nobody.
257
+ *
258
+ * A site that created the document (`hostId`) or captured the person on it
259
+ * (`capturedByHostIds`) holds its token whatever the grants say: a record
260
+ * shared with a site that then captures the person itself is that site's
261
+ * own from the capture on, before anything re-evaluates the grants.
262
+ */
263
+ export declare function heldScopeTokens(doc: Readonly<Record<string, unknown>> | null | undefined): string[];
264
+ /**
265
+ * Whether a site HOLDS a document rather than only seeing it through a
266
+ * grant. The question a marketing audience, a list rule, a sequence and a
267
+ * holder count ask; {@link visibleToHost} is the one a reader asks.
268
+ */
269
+ export declare function heldByHost(doc: Readonly<Record<string, unknown>> | null | undefined, hostId: string): boolean;
270
+ /**
271
+ * Whether a site sees a document ONLY through a grant: it carries grants and
272
+ * the site does not hold it. The filter an audience sweep applies to what
273
+ * its scoped query returned — a document with no grants answers `false`, so
274
+ * every read that predates sharing keeps exactly the rows it had.
275
+ */
276
+ export declare function seenOnlyThroughGrant(doc: Readonly<Record<string, unknown>> | null | undefined, hostId: string): boolean;
@@ -327,5 +327,66 @@ export function isScopeToken(value) {
327
327
  ...tail
328
328
  ].join('/');
329
329
  }
330
+ /*==========================================
331
+ * SEEN, AND HELD (AGL-3336).
332
+ *
333
+ * `visibleTo` answers one question, "who may see this", because it is the
334
+ * one field both enforcement layers can evaluate. A visibility GRANT — an
335
+ * org manager sharing a record with another site, by hand or by a rule —
336
+ * has to widen exactly that field, or the other site's lists, which are
337
+ * real `array-contains-any` queries, could never return the record.
338
+ *
339
+ * But a site a record was shared with does not HOLD it. It did not capture
340
+ * the person, it holds no consent from them, and letting go of the record
341
+ * is not its decision. So a document that carries a grant records which of
342
+ * its tokens the grant added, under `sharing.added`, and every question
343
+ * about holding — who may delete it, whose audience it joins, which site's
344
+ * list it enters — reads {@link heldScopeTokens} instead of `visibleTo`.
345
+ *
346
+ * A document without the field is held by everything that sees it, which
347
+ * is every document written before grants existed.
348
+ *=========================================*/ /** The map a scoped document records its visibility grants under. */ export const SCOPE_GRANTS_FIELD = 'sharing';
349
+ /** The tokens a grant ADDED to `visibleTo`: seen through, never held. */ export function grantedScopeTokens(doc) {
350
+ const grants = (doc != null ? doc : {})[SCOPE_GRANTS_FIELD];
351
+ if (!grants || typeof grants !== 'object' || Array.isArray(grants)) return [];
352
+ const added = grants['added'];
353
+ return Array.isArray(added) ? added.filter((token)=>typeof token === 'string') : [];
354
+ }
355
+ /**
356
+ * The tokens that HOLD a document: `visibleTo` without the ones a grant
357
+ * added. Missing `visibleTo` is held by nobody, as it is seen by nobody.
358
+ *
359
+ * A site that created the document (`hostId`) or captured the person on it
360
+ * (`capturedByHostIds`) holds its token whatever the grants say: a record
361
+ * shared with a site that then captures the person itself is that site's
362
+ * own from the capture on, before anything re-evaluates the grants.
363
+ */ export function heldScopeTokens(doc) {
364
+ const visibleTo = (doc != null ? doc : {})['visibleTo'];
365
+ if (!Array.isArray(visibleTo)) return [];
366
+ const granted = new Set(grantedScopeTokens(doc));
367
+ const capturing = new Set();
368
+ const hostId = (doc != null ? doc : {})['hostId'];
369
+ if (typeof hostId === 'string' && hostId) capturing.add(hostScopeToken(hostId));
370
+ const captured = (doc != null ? doc : {})['capturedByHostIds'];
371
+ if (Array.isArray(captured)) {
372
+ for (const id of captured)if (typeof id === 'string' && id) capturing.add(hostScopeToken(id));
373
+ }
374
+ return visibleTo.filter((token)=>typeof token === 'string' && (!granted.has(token) || capturing.has(token)));
375
+ }
376
+ /**
377
+ * Whether a site HOLDS a document rather than only seeing it through a
378
+ * grant. The question a marketing audience, a list rule, a sequence and a
379
+ * holder count ask; {@link visibleToHost} is the one a reader asks.
380
+ */ export function heldByHost(doc, hostId) {
381
+ return visibleToHost(heldScopeTokens(doc), hostId);
382
+ }
383
+ /**
384
+ * Whether a site sees a document ONLY through a grant: it carries grants and
385
+ * the site does not hold it. The filter an audience sweep applies to what
386
+ * its scoped query returned — a document with no grants answers `false`, so
387
+ * every read that predates sharing keeps exactly the rows it had.
388
+ */ export function seenOnlyThroughGrant(doc, hostId) {
389
+ return grantedScopeTokens(doc).length > 0 && !heldByHost(doc, hostId);
390
+ }
330
391
 
331
392
  //# sourceMappingURL=scope-tokens.js.map