@aglyn/aglyn 1.0.0-beta.221 → 1.0.0-beta.222

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 (346) hide show
  1. package/package.json +15 -11
  2. package/src/lib/app-utils/agent-openapi.js +1 -1
  3. package/src/lib/app-utils/agent-openapi.js.map +1 -1
  4. package/src/lib/app-utils/analytics-events.d.ts +3 -2
  5. package/src/lib/app-utils/analytics-events.js +3 -2
  6. package/src/lib/app-utils/analytics-events.js.map +1 -1
  7. package/src/lib/app-utils/api-plugins.d.ts +30 -0
  8. package/src/lib/app-utils/api-plugins.js +24 -1
  9. package/src/lib/app-utils/api-plugins.js.map +1 -1
  10. package/src/lib/app-utils/author-html.d.ts +1 -1
  11. package/src/lib/app-utils/author-html.js.map +1 -1
  12. package/src/lib/app-utils/bandwidth-cap.d.ts +12 -1
  13. package/src/lib/app-utils/bandwidth-cap.js +17 -1
  14. package/src/lib/app-utils/bandwidth-cap.js.map +1 -1
  15. package/src/lib/app-utils/compose-reusable-components.d.ts +19 -0
  16. package/src/lib/app-utils/compose-reusable-components.js +116 -26
  17. package/src/lib/app-utils/compose-reusable-components.js.map +1 -1
  18. package/src/lib/app-utils/console-routes.d.ts +4 -0
  19. package/src/lib/app-utils/console-routes.js +3 -0
  20. package/src/lib/app-utils/console-routes.js.map +1 -1
  21. package/src/lib/app-utils/contacts.d.ts +71 -0
  22. package/src/lib/app-utils/contacts.js +55 -0
  23. package/src/lib/app-utils/contacts.js.map +1 -1
  24. package/src/lib/app-utils/contexts.d.ts +1 -0
  25. package/src/lib/app-utils/contexts.js +1 -0
  26. package/src/lib/app-utils/contexts.js.map +1 -1
  27. package/src/lib/app-utils/crm-email-templates.js +21 -4
  28. package/src/lib/app-utils/crm-email-templates.js.map +1 -1
  29. package/src/lib/app-utils/crm-kinds.d.ts +2 -0
  30. package/src/lib/app-utils/crm-kinds.js +1 -0
  31. package/src/lib/app-utils/crm-kinds.js.map +1 -1
  32. package/src/lib/app-utils/crm.d.ts +1199 -67
  33. package/src/lib/app-utils/crm.js +1951 -310
  34. package/src/lib/app-utils/crm.js.map +1 -1
  35. package/src/lib/app-utils/csv-import.d.ts +10 -38
  36. package/src/lib/app-utils/csv-import.js +17 -42
  37. package/src/lib/app-utils/csv-import.js.map +1 -1
  38. package/src/lib/app-utils/csv.d.ts +32 -11
  39. package/src/lib/app-utils/csv.js +43 -5
  40. package/src/lib/app-utils/csv.js.map +1 -1
  41. package/src/lib/app-utils/default-site.d.ts +116 -0
  42. package/src/lib/app-utils/default-site.js +1024 -0
  43. package/src/lib/app-utils/default-site.js.map +1 -0
  44. package/src/lib/app-utils/docs-help.generated.d.ts +22 -22
  45. package/src/lib/app-utils/docs-help.generated.js +44 -10
  46. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  47. package/src/lib/app-utils/docs-index.generated.js +738 -150
  48. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  49. package/src/lib/app-utils/draft-placeholders.d.ts +76 -0
  50. package/src/lib/app-utils/draft-placeholders.js +115 -0
  51. package/src/lib/app-utils/draft-placeholders.js.map +1 -0
  52. package/src/lib/app-utils/entity-picker-context.d.ts +19 -15
  53. package/src/lib/app-utils/entity-picker-context.js +4 -4
  54. package/src/lib/app-utils/entity-picker-context.js.map +1 -1
  55. package/src/lib/app-utils/expand-repeatables.d.ts +66 -10
  56. package/src/lib/app-utils/expand-repeatables.js +88 -14
  57. package/src/lib/app-utils/expand-repeatables.js.map +1 -1
  58. package/src/lib/app-utils/forms.d.ts +33 -22
  59. package/src/lib/app-utils/forms.js +47 -28
  60. package/src/lib/app-utils/forms.js.map +1 -1
  61. package/src/lib/app-utils/full-use-cost.d.ts +277 -0
  62. package/src/lib/app-utils/full-use-cost.js +421 -0
  63. package/src/lib/app-utils/full-use-cost.js.map +1 -0
  64. package/src/lib/app-utils/health-report.js +11 -0
  65. package/src/lib/app-utils/health-report.js.map +1 -1
  66. package/src/lib/app-utils/llms-txt.d.ts +20 -0
  67. package/src/lib/app-utils/llms-txt.js +7 -1
  68. package/src/lib/app-utils/llms-txt.js.map +1 -1
  69. package/src/lib/app-utils/media-asset-facts.d.ts +16 -1
  70. package/src/lib/app-utils/media-asset-facts.js +40 -13
  71. package/src/lib/app-utils/media-asset-facts.js.map +1 -1
  72. package/src/lib/app-utils/media-bandwidth.d.ts +52 -0
  73. package/src/lib/app-utils/media-bandwidth.js +94 -0
  74. package/src/lib/app-utils/media-bandwidth.js.map +1 -0
  75. package/src/lib/app-utils/media-ref.d.ts +241 -33
  76. package/src/lib/app-utils/media-ref.js +324 -43
  77. package/src/lib/app-utils/media-ref.js.map +1 -1
  78. package/src/lib/app-utils/media-src.d.ts +8 -8
  79. package/src/lib/app-utils/media-src.js +27 -11
  80. package/src/lib/app-utils/media-src.js.map +1 -1
  81. package/src/lib/app-utils/media-takedown-reach.d.ts +4 -1
  82. package/src/lib/app-utils/media-takedown-reach.js +6 -3
  83. package/src/lib/app-utils/media-takedown-reach.js.map +1 -1
  84. package/src/lib/app-utils/notifications.d.ts +1 -1
  85. package/src/lib/app-utils/notifications.js +2 -0
  86. package/src/lib/app-utils/notifications.js.map +1 -1
  87. package/src/lib/app-utils/organizations.d.ts +51 -2
  88. package/src/lib/app-utils/organizations.js +56 -3
  89. package/src/lib/app-utils/organizations.js.map +1 -1
  90. package/src/lib/app-utils/page-record-sources.d.ts +87 -0
  91. package/src/lib/app-utils/page-record-sources.js +53 -0
  92. package/src/lib/app-utils/page-record-sources.js.map +1 -0
  93. package/src/lib/app-utils/paid-media-source.d.ts +3 -7
  94. package/src/lib/app-utils/paid-media-source.js +5 -19
  95. package/src/lib/app-utils/paid-media-source.js.map +1 -1
  96. package/src/lib/app-utils/person-erasure.d.ts +5 -2
  97. package/src/lib/app-utils/person-erasure.js.map +1 -1
  98. package/src/lib/app-utils/picklists.d.ts +187 -0
  99. package/src/lib/app-utils/picklists.js +320 -0
  100. package/src/lib/app-utils/picklists.js.map +1 -0
  101. package/src/lib/app-utils/plan-entitlements.d.ts +220 -50
  102. package/src/lib/app-utils/plan-entitlements.js +309 -115
  103. package/src/lib/app-utils/plan-entitlements.js.map +1 -1
  104. package/src/lib/app-utils/realm-host-surface.generated.js +2 -0
  105. package/src/lib/app-utils/realm-host-surface.generated.js.map +1 -1
  106. package/src/lib/app-utils/release-flags.d.ts +1 -1
  107. package/src/lib/app-utils/release-flags.js +11 -0
  108. package/src/lib/app-utils/release-flags.js.map +1 -1
  109. package/src/lib/app-utils/rendered-widths.d.ts +121 -0
  110. package/src/lib/app-utils/rendered-widths.js +211 -0
  111. package/src/lib/app-utils/rendered-widths.js.map +1 -0
  112. package/src/lib/app-utils/repeat-sources.d.ts +2 -1
  113. package/src/lib/app-utils/repeat-sources.js.map +1 -1
  114. package/src/lib/app-utils/screen-route.d.ts +154 -36
  115. package/src/lib/app-utils/screen-route.js +185 -40
  116. package/src/lib/app-utils/screen-route.js.map +1 -1
  117. package/src/lib/app-utils/seo-audit.d.ts +11 -1
  118. package/src/lib/app-utils/seo-audit.js +33 -13
  119. package/src/lib/app-utils/seo-audit.js.map +1 -1
  120. package/src/lib/app-utils/seo-keywords.d.ts +11 -0
  121. package/src/lib/app-utils/seo-keywords.js +19 -5
  122. package/src/lib/app-utils/seo-keywords.js.map +1 -1
  123. package/src/lib/app-utils/seo-page-facts.d.ts +47 -4
  124. package/src/lib/app-utils/seo-page-facts.js +136 -31
  125. package/src/lib/app-utils/seo-page-facts.js.map +1 -1
  126. package/src/lib/app-utils/seo-site-scan.d.ts +42 -4
  127. package/src/lib/app-utils/seo-site-scan.js +113 -12
  128. package/src/lib/app-utils/seo-site-scan.js.map +1 -1
  129. package/src/lib/app-utils/server.d.ts +5 -4
  130. package/src/lib/app-utils/server.js +14 -5
  131. package/src/lib/app-utils/server.js.map +1 -1
  132. package/src/lib/app-utils/site-icon-set.d.ts +195 -0
  133. package/src/lib/app-utils/site-icon-set.js +351 -0
  134. package/src/lib/app-utils/site-icon-set.js.map +1 -0
  135. package/src/lib/app-utils/site-interactions.d.ts +101 -4
  136. package/src/lib/app-utils/site-interactions.js +120 -4
  137. package/src/lib/app-utils/site-interactions.js.map +1 -1
  138. package/src/lib/app-utils/starter-template-nodes.d.ts +7 -0
  139. package/src/lib/app-utils/starter-template-nodes.js +3 -0
  140. package/src/lib/app-utils/starter-template-nodes.js.map +1 -1
  141. package/src/lib/app-utils/starter-templates.d.ts +5 -19
  142. package/src/lib/app-utils/starter-templates.js +5 -49
  143. package/src/lib/app-utils/starter-templates.js.map +1 -1
  144. package/src/lib/app-utils/transfer-launcher-context.d.ts +136 -0
  145. package/src/lib/app-utils/transfer-launcher-context.js +23 -0
  146. package/src/lib/app-utils/transfer-launcher-context.js.map +1 -0
  147. package/src/lib/app-utils/transfer-resume-import.d.ts +39 -0
  148. package/src/lib/app-utils/transfer-resume-import.js +128 -0
  149. package/src/lib/app-utils/transfer-resume-import.js.map +1 -0
  150. package/src/lib/app-utils/upgrade-proposal.d.ts +66 -0
  151. package/src/lib/app-utils/upgrade-proposal.js +95 -0
  152. package/src/lib/app-utils/upgrade-proposal.js.map +1 -0
  153. package/src/lib/app-utils/utc-month.d.ts +33 -0
  154. package/src/lib/app-utils/utc-month.js +35 -0
  155. package/src/lib/app-utils/utc-month.js.map +1 -0
  156. package/src/lib/app-utils/visitor-record-ceiling.d.ts +4 -4
  157. package/src/lib/app-utils/visitor-record-ceiling.js +4 -4
  158. package/src/lib/app-utils/visitor-record-ceiling.js.map +1 -1
  159. package/src/lib/app-utils/wire-nodes.d.ts +39 -19
  160. package/src/lib/app-utils/wire-nodes.js +120 -34
  161. package/src/lib/app-utils/wire-nodes.js.map +1 -1
  162. package/src/lib/data-transfer/access.d.ts +74 -0
  163. package/src/lib/data-transfer/access.js +93 -0
  164. package/src/lib/data-transfer/access.js.map +1 -0
  165. package/src/lib/data-transfer/derive.d.ts +246 -0
  166. package/src/lib/data-transfer/derive.js +1007 -0
  167. package/src/lib/data-transfer/derive.js.map +1 -0
  168. package/src/lib/data-transfer/export-file.d.ts +46 -0
  169. package/src/lib/data-transfer/export-file.js +146 -0
  170. package/src/lib/data-transfer/export-file.js.map +1 -0
  171. package/src/lib/data-transfer/field-catalog.d.ts +134 -0
  172. package/src/lib/data-transfer/field-catalog.js +278 -0
  173. package/src/lib/data-transfer/field-catalog.js.map +1 -0
  174. package/src/lib/data-transfer/header-match.d.ts +129 -0
  175. package/src/lib/data-transfer/header-match.js +367 -0
  176. package/src/lib/data-transfer/header-match.js.map +1 -0
  177. package/src/lib/data-transfer/index.d.ts +34 -0
  178. package/src/lib/data-transfer/index.js +40 -0
  179. package/src/lib/data-transfer/index.js.map +1 -0
  180. package/src/lib/data-transfer/job.d.ts +190 -0
  181. package/src/lib/data-transfer/job.js +229 -0
  182. package/src/lib/data-transfer/job.js.map +1 -0
  183. package/src/lib/data-transfer/lookup.d.ts +45 -0
  184. package/src/lib/data-transfer/lookup.js +71 -0
  185. package/src/lib/data-transfer/lookup.js.map +1 -0
  186. package/src/lib/data-transfer/match.d.ts +99 -0
  187. package/src/lib/data-transfer/match.js +244 -0
  188. package/src/lib/data-transfer/match.js.map +1 -0
  189. package/src/lib/data-transfer/package-plan.d.ts +205 -0
  190. package/src/lib/data-transfer/package-plan.js +475 -0
  191. package/src/lib/data-transfer/package-plan.js.map +1 -0
  192. package/src/lib/data-transfer/package.d.ts +181 -0
  193. package/src/lib/data-transfer/package.js +426 -0
  194. package/src/lib/data-transfer/package.js.map +1 -0
  195. package/src/lib/data-transfer/picklist-map.d.ts +139 -0
  196. package/src/lib/data-transfer/picklist-map.js +271 -0
  197. package/src/lib/data-transfer/picklist-map.js.map +1 -0
  198. package/src/lib/data-transfer/plan.d.ts +176 -0
  199. package/src/lib/data-transfer/plan.js +451 -0
  200. package/src/lib/data-transfer/plan.js.map +1 -0
  201. package/src/lib/data-transfer/policy.d.ts +170 -0
  202. package/src/lib/data-transfer/policy.js +378 -0
  203. package/src/lib/data-transfer/policy.js.map +1 -0
  204. package/src/lib/data-transfer/resource.d.ts +211 -0
  205. package/src/lib/data-transfer/resource.js +241 -0
  206. package/src/lib/data-transfer/resource.js.map +1 -0
  207. package/src/lib/data-transfer/review.d.ts +70 -0
  208. package/src/lib/data-transfer/review.js +233 -0
  209. package/src/lib/data-transfer/review.js.map +1 -0
  210. package/src/lib/data-transfer/similarity.d.ts +40 -0
  211. package/src/lib/data-transfer/similarity.js +106 -0
  212. package/src/lib/data-transfer/similarity.js.map +1 -0
  213. package/src/lib/data-transfer/site-package.d.ts +365 -0
  214. package/src/lib/data-transfer/site-package.js +1077 -0
  215. package/src/lib/data-transfer/site-package.js.map +1 -0
  216. package/src/lib/data-transfer/source.d.ts +74 -0
  217. package/src/lib/data-transfer/source.js +293 -0
  218. package/src/lib/data-transfer/source.js.map +1 -0
  219. package/src/lib/data-transfer/transfer-api.d.ts +898 -0
  220. package/src/lib/data-transfer/transfer-api.js +107 -0
  221. package/src/lib/data-transfer/transfer-api.js.map +1 -0
  222. package/src/lib/foundation/definitions/org-billing.types.d.ts +31 -0
  223. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
  224. package/src/lib/foundation/definitions/organization.types.d.ts +42 -1
  225. package/src/lib/foundation/definitions/organization.types.js.map +1 -1
  226. package/src/lib/foundation/definitions/platform.types.d.ts +6 -4
  227. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  228. package/src/lib/foundation/index.d.ts +1 -1
  229. package/src/lib/foundation/index.js.map +1 -1
  230. package/src/lib/plugin-manager/feature-plugins.d.ts +91 -2
  231. package/src/lib/plugin-manager/feature-plugins.js +48 -3
  232. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  233. package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +50 -0
  234. package/src/lib/plugin-manager/first-party-plugins.generated.js +1129 -28
  235. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  236. package/src/lib/plugin-manager/index.d.ts +0 -1
  237. package/src/lib/plugin-manager/index.js +0 -1
  238. package/src/lib/plugin-manager/index.js.map +1 -1
  239. package/src/lib/plugin-manager/interaction-recipes.d.ts +142 -0
  240. package/src/lib/plugin-manager/interaction-recipes.js +104 -0
  241. package/src/lib/plugin-manager/interaction-recipes.js.map +1 -0
  242. package/src/lib/plugin-manager/interaction-step-checks.d.ts +66 -0
  243. package/src/lib/plugin-manager/interaction-step-checks.js +86 -0
  244. package/src/lib/plugin-manager/interaction-step-checks.js.map +1 -0
  245. package/src/lib/plugin-manager/plugin-artifact-types.d.ts +235 -0
  246. package/src/lib/plugin-manager/plugin-artifact-types.js +99 -0
  247. package/src/lib/plugin-manager/plugin-artifact-types.js.map +1 -0
  248. package/src/lib/plugin-manager/plugin-contact-capture.d.ts +8 -0
  249. package/src/lib/plugin-manager/plugin-contact-capture.js.map +1 -1
  250. package/src/lib/plugin-manager/plugin-conversion-credit.d.ts +216 -0
  251. package/src/lib/plugin-manager/plugin-conversion-credit.js +91 -0
  252. package/src/lib/plugin-manager/plugin-conversion-credit.js.map +1 -0
  253. package/src/lib/plugin-manager/plugin-entity-pickers.d.ts +83 -0
  254. package/src/lib/plugin-manager/plugin-entity-pickers.js +87 -0
  255. package/src/lib/plugin-manager/plugin-entity-pickers.js.map +1 -0
  256. package/src/lib/plugin-manager/plugin-host-resources.d.ts +6 -0
  257. package/src/lib/plugin-manager/plugin-host-resources.js +6 -1
  258. package/src/lib/plugin-manager/plugin-host-resources.js.map +1 -1
  259. package/src/lib/plugin-manager/plugin-intake-gates.d.ts +75 -0
  260. package/src/lib/plugin-manager/plugin-intake-gates.js +49 -0
  261. package/src/lib/plugin-manager/plugin-intake-gates.js.map +1 -0
  262. package/src/lib/plugin-manager/plugin-jobs.d.ts +5 -5
  263. package/src/lib/plugin-manager/plugin-jobs.js +7 -7
  264. package/src/lib/plugin-manager/plugin-jobs.js.map +1 -1
  265. package/src/lib/plugin-manager/plugin-live-paths.d.ts +70 -0
  266. package/src/lib/plugin-manager/plugin-live-paths.js +66 -0
  267. package/src/lib/plugin-manager/plugin-live-paths.js.map +1 -0
  268. package/src/lib/plugin-manager/plugin-org-erasure.d.ts +3 -2
  269. package/src/lib/plugin-manager/plugin-org-erasure.js +30 -5
  270. package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -1
  271. package/src/lib/plugin-manager/plugin-person-erasure.d.ts +68 -9
  272. package/src/lib/plugin-manager/plugin-person-erasure.js +196 -51
  273. package/src/lib/plugin-manager/plugin-person-erasure.js.map +1 -1
  274. package/src/lib/plugin-manager/plugin-person-records.d.ts +275 -0
  275. package/src/lib/plugin-manager/plugin-person-records.js +107 -0
  276. package/src/lib/plugin-manager/plugin-person-records.js.map +1 -0
  277. package/src/lib/plugin-manager/plugin-record-counts.d.ts +76 -0
  278. package/src/lib/plugin-manager/plugin-record-counts.js +49 -0
  279. package/src/lib/plugin-manager/plugin-record-counts.js.map +1 -0
  280. package/src/lib/plugin-manager/plugin-record-email-state.d.ts +26 -0
  281. package/src/lib/plugin-manager/plugin-record-email-state.js +16 -0
  282. package/src/lib/plugin-manager/plugin-record-email-state.js.map +1 -1
  283. package/src/lib/plugin-manager/plugin-record-index.d.ts +10 -0
  284. package/src/lib/plugin-manager/plugin-record-index.js.map +1 -1
  285. package/src/lib/plugin-manager/plugin-record-lists.d.ts +86 -6
  286. package/src/lib/plugin-manager/plugin-record-lists.js +42 -5
  287. package/src/lib/plugin-manager/plugin-record-lists.js.map +1 -1
  288. package/src/lib/plugin-manager/plugin-record-origin.d.ts +79 -0
  289. package/src/lib/plugin-manager/plugin-record-origin.js +46 -0
  290. package/src/lib/plugin-manager/plugin-record-origin.js.map +1 -0
  291. package/src/lib/plugin-manager/plugin-record-pages.d.ts +37 -0
  292. package/src/lib/plugin-manager/plugin-record-pages.js +52 -0
  293. package/src/lib/plugin-manager/plugin-record-pages.js.map +1 -0
  294. package/src/lib/plugin-manager/plugin-record-timeline.d.ts +60 -0
  295. package/src/lib/plugin-manager/plugin-record-timeline.js +16 -0
  296. package/src/lib/plugin-manager/plugin-record-timeline.js.map +1 -1
  297. package/src/lib/plugin-manager/plugin-resource-drafts.d.ts +5 -5
  298. package/src/lib/plugin-manager/plugin-resource-drafts.js.map +1 -1
  299. package/src/lib/plugin-manager/plugin-revocations.d.ts +64 -0
  300. package/src/lib/plugin-manager/plugin-revocations.js +47 -0
  301. package/src/lib/plugin-manager/plugin-revocations.js.map +1 -0
  302. package/src/lib/plugin-manager/plugin-send-tallies.d.ts +69 -0
  303. package/src/lib/plugin-manager/plugin-send-tallies.js +45 -0
  304. package/src/lib/plugin-manager/plugin-send-tallies.js.map +1 -0
  305. package/src/lib/plugin-manager/plugin-site-bundle.d.ts +40 -0
  306. package/src/lib/plugin-manager/plugin-site-bundle.js +4 -0
  307. package/src/lib/plugin-manager/plugin-site-bundle.js.map +1 -1
  308. package/src/lib/plugin-manager/plugin-site-export.d.ts +57 -1
  309. package/src/lib/plugin-manager/plugin-site-export.js +23 -10
  310. package/src/lib/plugin-manager/plugin-site-export.js.map +1 -1
  311. package/src/lib/plugin-manager/plugin-sitemap-readers.d.ts +146 -0
  312. package/src/lib/plugin-manager/plugin-sitemap-readers.js +110 -0
  313. package/src/lib/plugin-manager/plugin-sitemap-readers.js.map +1 -0
  314. package/src/lib/plugin-manager/plugin-transfer-resources.d.ts +714 -0
  315. package/src/lib/plugin-manager/plugin-transfer-resources.js +443 -0
  316. package/src/lib/plugin-manager/plugin-transfer-resources.js.map +1 -0
  317. package/src/lib/plugin-manager/plugin-usage-axes.d.ts +54 -5
  318. package/src/lib/plugin-manager/plugin-usage-axes.js +11 -3
  319. package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -1
  320. package/src/lib/plugin-manager/plugin-user-erasure.js +9 -1
  321. package/src/lib/plugin-manager/plugin-user-erasure.js.map +1 -1
  322. package/src/lib/plugin-manager/plugin-visitor-doors.d.ts +104 -0
  323. package/src/lib/plugin-manager/plugin-visitor-doors.js +108 -0
  324. package/src/lib/plugin-manager/plugin-visitor-doors.js.map +1 -0
  325. package/src/lib/plugin-manager/realm-host-aglyn.generated.js +2 -0
  326. package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -1
  327. package/src/lib/plugin-manager/repeat-rows.d.ts +5 -6
  328. package/src/lib/plugin-manager/repeat-rows.js.map +1 -1
  329. package/src/lib/app-utils/actions.d.ts +0 -310
  330. package/src/lib/app-utils/actions.js +0 -506
  331. package/src/lib/app-utils/actions.js.map +0 -1
  332. package/src/lib/app-utils/automation-placeholders.d.ts +0 -72
  333. package/src/lib/app-utils/automation-placeholders.js +0 -207
  334. package/src/lib/app-utils/automation-placeholders.js.map +0 -1
  335. package/src/lib/app-utils/dataset-models.d.ts +0 -339
  336. package/src/lib/app-utils/dataset-models.js +0 -722
  337. package/src/lib/app-utils/dataset-models.js.map +0 -1
  338. package/src/lib/app-utils/datasets.d.ts +0 -171
  339. package/src/lib/app-utils/datasets.js +0 -227
  340. package/src/lib/app-utils/datasets.js.map +0 -1
  341. package/src/lib/app-utils/form-abuse-ceiling.d.ts +0 -146
  342. package/src/lib/app-utils/form-abuse-ceiling.js +0 -158
  343. package/src/lib/app-utils/form-abuse-ceiling.js.map +0 -1
  344. package/src/lib/plugin-manager/order-fulfilment.d.ts +0 -142
  345. package/src/lib/plugin-manager/order-fulfilment.js +0 -104
  346. package/src/lib/plugin-manager/order-fulfilment.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-email-state.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 { EmailState } from '../app-utils/email-state'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * The verdict on an address, written onto the records that carry it\n * (AGL-3245).\n *\n * The senders keep their lists — the platform's suppression list, a site's\n * suppressions, an organization's do-not-contact list — and those are what\n * a send consults. The record a person reads is the record system's: a\n * lead, a contact, whatever the plugin that keeps the workspace's records\n * calls them. A sender must not write that plugin's documents itself, nor\n * import it; so the record system registers a WRITER here, and a sender\n * that filed a verdict on a list says so through it.\n *\n * The writer finds every record the address is — the plugin knows its own\n * storage and its own index — and stamps `emailState` on each, keeping the\n * stronger verdict (`nextEmailState`). A single-implementation contract: a\n * workspace keeps one record system.\n *\n * ## The caller proves who is asking\n *\n * Like the timeline seam, the registry authenticates nobody. The caller\n * decides, in its own terms and before it asks, that the verdict is the\n * workspace's to write: an address it sent to, a list it wrote.\n */\n\nexport interface PluginRecordEmailStateRequest {\n /** The organization whose records are stamped. */\n orgId?: string\n /**\n * Or the site the send left from, when the caller knows only that — a\n * campaign webhook, an unsubscribe link. The writer reads the\n * organization off it.\n */\n hostId?: string\n email: string\n state: EmailState\n /** Write the state whatever the record holds — a release. */\n force?: boolean\n /**\n * Take the verdict back instead of writing it: a record whose standing\n * state has `state.status` loses it, and any other record is left alone.\n * The deliverability check's way back from `undeliverable` once the\n * domain takes mail (AGL-3328); it never clears a verdict it did not give.\n */\n withdraw?: boolean\n}\n\nexport interface PluginRecordEmailStateReport {\n /** Records whose state moved. */\n records: number\n}\n\n/**\n * One delivery event a send's webhook recorded, as the delivery log\n * answered it: who it is about, what happened, when, and whether it is the\n * first event of its type for its message.\n */\nexport interface PluginRecordEmailEvent {\n /** The recipient, lowercased. */\n to: string\n /** `opened`, `clicked`, `delivered`, `bounced`, … */\n type: string\n /** When it happened, epoch ms. */\n at: number\n /** No event of this type had been recorded against this message before. */\n firstOfType: boolean\n}\n\n/**\n * The events one webhook delivery recorded for a send from a site — what\n * the record system reads a person's engagement off (AGL-2616).\n */\nexport interface PluginRecordEmailEngagementRequest {\n /**\n * The site the send went out from: the `hostId` tag the send stamped,\n * which is the only tenant identity a delivery event carries.\n */\n hostId: string\n events: readonly PluginRecordEmailEvent[]\n}\n\n/**\n * The people a marketing send was delivered to (AGL-3446), so the record\n * system can say automated email is reaching them — a lead's Nurturing\n * stage. Only what was actually accepted for delivery: a refused or\n * deferred address reached nobody.\n */\nexport interface PluginRecordEmailReachRequest {\n /** The organization whose records the send reached. */\n orgId: string\n /** The site the send went out as; only records it holds are moved. */\n hostId: string\n /** The addresses delivered to, as the send addressed them. */\n emails: readonly string[]\n}\n\nexport interface PluginRecordEmailStateWriter {\n /** Never throws: a list is the control, the stamp is what a person reads. */\n stamp(request: PluginRecordEmailStateRequest): Promise<PluginRecordEmailStateReport>\n /**\n * Stamps when each person these events name last engaged with a send from\n * the site, on the records the site may see. Optional: a record system\n * that keeps no engagement answers nothing. Never throws, for the reason\n * `stamp` does not: the webhook's acknowledgement must not depend on it.\n */\n engaged?(request: PluginRecordEmailEngagementRequest): Promise<PluginRecordEmailStateReport>\n /**\n * Notes that a marketing send reached these people. Optional: a record\n * system with no stage for it answers nothing. Never throws: the mail has\n * already gone.\n */\n reached?(request: PluginRecordEmailReachRequest): Promise<PluginRecordEmailStateReport>\n}\n\nexport const PLUGIN_RECORD_EMAIL_STATE = definePluginServiceContract<PluginRecordEmailStateWriter>(\n 'core.record-email-state',\n { multiple: false },\n)\n\n/**\n * Registers the workspace's record email-state writer. The owner is the\n * loader's marker when a register fn is running, else `options.pluginId`;\n * with neither the registration throws, and a second plugin's writer is\n * refused naming both — the incumbent keeps serving.\n */\nexport function registerPluginRecordEmailStateWriter(\n writer: PluginRecordEmailStateWriter,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_RECORD_EMAIL_STATE, writer, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\nexport interface ResolvedPluginRecordEmailStateWriter {\n /** The plugin that keeps the records. */\n pluginId: string\n writer: PluginRecordEmailStateWriter\n}\n\n/** The writer, with its owner, or `null` when no plugin keeps records here. */\nexport function pluginRecordEmailStateWriter(): ResolvedPluginRecordEmailStateWriter | null {\n const entry = resolvePluginServices(PLUGIN_RECORD_EMAIL_STATE)[0]\n return entry ? { pluginId: entry.pluginId, writer: entry.impl } : null\n}\n\n/**\n * Stamps the verdict through whichever plugin keeps the records, or answers\n * `null` when none does. Never throws: every caller is a sender for whom\n * the stamp is a courtesy beside the list it already wrote.\n */\nexport async function stampRecordEmailState(\n request: PluginRecordEmailStateRequest,\n): Promise<PluginRecordEmailStateReport | null> {\n const resolved = pluginRecordEmailStateWriter()\n if (!resolved) return null\n try {\n return await resolved.writer.stamp(request)\n } catch (error) {\n console.error('[record-email-state] the record system could not stamp the address', error)\n return null\n }\n}\n\n/**\n * Hands a send's delivery events to whichever plugin keeps the records, so\n * it can stamp the people who engaged (AGL-2616). Answers `null` when no\n * plugin keeps records, or the one that does keeps no engagement. Never\n * throws: the caller is a webhook whose acknowledgement must not hang on a\n * courtesy stamp.\n */\nexport async function stampRecordEmailEngagement(\n request: PluginRecordEmailEngagementRequest,\n): Promise<PluginRecordEmailStateReport | null> {\n const resolved = pluginRecordEmailStateWriter()\n if (!resolved?.writer.engaged) return null\n try {\n return await resolved.writer.engaged(request)\n } catch (error) {\n console.error('[record-email-state] the record system could not stamp an engagement', error)\n return null\n }\n}\n\n/**\n * Hands the addresses a marketing send was delivered to to whichever plugin\n * keeps the records (AGL-3446). Answers `null` when no plugin keeps\n * records, or the one that does keeps no such stage. Never throws: the\n * send has already delivered, and its bookkeeping must not fail it.\n */\nexport async function stampRecordEmailReach(\n request: PluginRecordEmailReachRequest,\n): Promise<PluginRecordEmailStateReport | null> {\n if (!request.emails.length) return null\n const resolved = pluginRecordEmailStateWriter()\n if (!resolved?.writer.reached) return null\n try {\n return await resolved.writer.reached(request)\n } catch (error) {\n console.error('[record-email-state] the record system could not note a delivered send', error)\n return null\n }\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_EMAIL_STATE","multiple","registerPluginRecordEmailStateWriter","writer","options","pluginId","pluginRecordEmailStateWriter","entry","impl","stampRecordEmailState","request","resolved","stamp","error","console","stampRecordEmailEngagement","engaged","stampRecordEmailReach","emails","length","reached"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAmH1B,OAAO,MAAMC,4BAA4BH,4BACvC,2BACA;IAAEI,UAAU;AAAM,GACnB;AAED;;;;;CAKC,GACD,OAAO,SAASC,qCACdC,MAAoC,EACpCC,OAA+B;IAE/BN,sBAAsBE,2BAA2BG,QAAQ,aACnDC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAQA,6EAA6E,GAC7E,OAAO,SAASC;IACd,MAAMC,QAAQR,sBAAsBC,0BAA0B,CAAC,EAAE;IACjE,OAAOO,QAAQ;QAAEF,UAAUE,MAAMF,QAAQ;QAAEF,QAAQI,MAAMC,IAAI;IAAC,IAAI;AACpE;AAEA;;;;CAIC,GACD,OAAO,eAAeC,sBACpBC,OAAsC;IAEtC,MAAMC,WAAWL;IACjB,IAAI,CAACK,UAAU,OAAO;IACtB,IAAI;QACF,OAAO,MAAMA,SAASR,MAAM,CAACS,KAAK,CAACF;IACrC,EAAE,OAAOG,OAAO;QACdC,QAAQD,KAAK,CAAC,sEAAsEA;QACpF,OAAO;IACT;AACF;AAEA;;;;;;CAMC,GACD,OAAO,eAAeE,2BACpBL,OAA2C;IAE3C,MAAMC,WAAWL;IACjB,IAAI,EAACK,4BAAAA,SAAUR,MAAM,CAACa,OAAO,GAAE,OAAO;IACtC,IAAI;QACF,OAAO,MAAML,SAASR,MAAM,CAACa,OAAO,CAACN;IACvC,EAAE,OAAOG,OAAO;QACdC,QAAQD,KAAK,CAAC,wEAAwEA;QACtF,OAAO;IACT;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAeI,sBACpBP,OAAsC;IAEtC,IAAI,CAACA,QAAQQ,MAAM,CAACC,MAAM,EAAE,OAAO;IACnC,MAAMR,WAAWL;IACjB,IAAI,EAACK,4BAAAA,SAAUR,MAAM,CAACiB,OAAO,GAAE,OAAO;IACtC,IAAI;QACF,OAAO,MAAMT,SAASR,MAAM,CAACiB,OAAO,CAACV;IACvC,EAAE,OAAOG,OAAO;QACdC,QAAQD,KAAK,CAAC,0EAA0EA;QACxF,OAAO;IACT;AACF"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-email-state.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 { EmailState } from '../app-utils/email-state'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * The verdict on an address, written onto the records that carry it\n * (AGL-3245).\n *\n * The senders keep their lists — the platform's suppression list, a site's\n * suppressions, an organization's do-not-contact list — and those are what\n * a send consults. The record a person reads is the record system's: a\n * lead, a contact, whatever the plugin that keeps the workspace's records\n * calls them. A sender must not write that plugin's documents itself, nor\n * import it; so the record system registers a WRITER here, and a sender\n * that filed a verdict on a list says so through it.\n *\n * The writer finds every record the address is — the plugin knows its own\n * storage and its own index — and stamps `emailState` on each, keeping the\n * stronger verdict (`nextEmailState`). A single-implementation contract: a\n * workspace keeps one record system.\n *\n * ## The caller proves who is asking\n *\n * Like the timeline seam, the registry authenticates nobody. The caller\n * decides, in its own terms and before it asks, that the verdict is the\n * workspace's to write: an address it sent to, a list it wrote.\n */\n\nexport interface PluginRecordEmailStateRequest {\n /** The organization whose records are stamped. */\n orgId?: string\n /**\n * Or the site the send left from, when the caller knows only that — a\n * campaign webhook, an unsubscribe link. The writer reads the\n * organization off it.\n */\n hostId?: string\n email: string\n state: EmailState\n /** Write the state whatever the record holds — a release. */\n force?: boolean\n /**\n * Take the verdict back instead of writing it: a record whose standing\n * state has `state.status` loses it, and any other record is left alone.\n * The deliverability check's way back from `undeliverable` once the\n * domain takes mail (AGL-3328); it never clears a verdict it did not give.\n */\n withdraw?: boolean\n}\n\nexport interface PluginRecordEmailStateReport {\n /** Records whose state moved. */\n records: number\n}\n\n/**\n * One delivery event a send's webhook recorded, as the delivery log\n * answered it: who it is about, what happened, when, and whether it is the\n * first event of its type for its message.\n */\nexport interface PluginRecordEmailEvent {\n /** The recipient, lowercased. */\n to: string\n /** `opened`, `clicked`, `delivered`, `bounced`, … */\n type: string\n /** When it happened, epoch ms. */\n at: number\n /** No event of this type had been recorded against this message before. */\n firstOfType: boolean\n}\n\n/**\n * The events one webhook delivery recorded for a send from a site — what\n * the record system reads a person's engagement off (AGL-2616).\n */\nexport interface PluginRecordEmailEngagementRequest {\n /**\n * The site the send went out from: the `hostId` tag the send stamped,\n * which is the only tenant identity a delivery event carries.\n */\n hostId: string\n events: readonly PluginRecordEmailEvent[]\n}\n\n/**\n * The people a marketing send was delivered to (AGL-3446), so the record\n * system can say automated email is reaching them — a lead's Nurturing\n * stage. Only what was actually accepted for delivery: a refused or\n * deferred address reached nobody.\n */\nexport interface PluginRecordEmailReachRequest {\n /** The organization whose records the send reached. */\n orgId: string\n /** The site the send went out as; only records it holds are moved. */\n hostId: string\n /** The addresses delivered to, as the send addressed them. */\n emails: readonly string[]\n}\n\n/**\n * The people who wrote back to mail the workspace sent them (AGL-3080), so\n * the record system can say a person is engaged — a lead nobody had worked is\n * being worked now. Only a real reply: an opt-out answered with \"remove me\"\n * is a refusal, filed through the suppression lists, and is not handed here.\n */\nexport interface PluginRecordEmailReplyRequest {\n /** The organization whose records the reply is about. */\n orgId: string\n /** The site the mail went out as; only records it holds are moved. */\n hostId: string\n /** The addresses that wrote back, as the mail addressed them. */\n emails: readonly string[]\n}\n\nexport interface PluginRecordEmailStateWriter {\n /** Never throws: a list is the control, the stamp is what a person reads. */\n stamp(request: PluginRecordEmailStateRequest): Promise<PluginRecordEmailStateReport>\n /**\n * Stamps when each person these events name last engaged with a send from\n * the site, on the records the site may see. Optional: a record system\n * that keeps no engagement answers nothing. Never throws, for the reason\n * `stamp` does not: the webhook's acknowledgement must not depend on it.\n */\n engaged?(request: PluginRecordEmailEngagementRequest): Promise<PluginRecordEmailStateReport>\n /**\n * Notes that a marketing send reached these people. Optional: a record\n * system with no stage for it answers nothing. Never throws: the mail has\n * already gone.\n */\n reached?(request: PluginRecordEmailReachRequest): Promise<PluginRecordEmailStateReport>\n /**\n * Notes that these people replied. Optional: a record system with no stage\n * for it answers nothing. Never throws: the reply has already arrived.\n */\n replied?(request: PluginRecordEmailReplyRequest): Promise<PluginRecordEmailStateReport>\n}\n\nexport const PLUGIN_RECORD_EMAIL_STATE = definePluginServiceContract<PluginRecordEmailStateWriter>(\n 'core.record-email-state',\n { multiple: false },\n)\n\n/**\n * Registers the workspace's record email-state writer. The owner is the\n * loader's marker when a register fn is running, else `options.pluginId`;\n * with neither the registration throws, and a second plugin's writer is\n * refused naming both — the incumbent keeps serving.\n */\nexport function registerPluginRecordEmailStateWriter(\n writer: PluginRecordEmailStateWriter,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_RECORD_EMAIL_STATE, writer, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\nexport interface ResolvedPluginRecordEmailStateWriter {\n /** The plugin that keeps the records. */\n pluginId: string\n writer: PluginRecordEmailStateWriter\n}\n\n/** The writer, with its owner, or `null` when no plugin keeps records here. */\nexport function pluginRecordEmailStateWriter(): ResolvedPluginRecordEmailStateWriter | null {\n const entry = resolvePluginServices(PLUGIN_RECORD_EMAIL_STATE)[0]\n return entry ? { pluginId: entry.pluginId, writer: entry.impl } : null\n}\n\n/**\n * Stamps the verdict through whichever plugin keeps the records, or answers\n * `null` when none does. Never throws: every caller is a sender for whom\n * the stamp is a courtesy beside the list it already wrote.\n */\nexport async function stampRecordEmailState(\n request: PluginRecordEmailStateRequest,\n): Promise<PluginRecordEmailStateReport | null> {\n const resolved = pluginRecordEmailStateWriter()\n if (!resolved) return null\n try {\n return await resolved.writer.stamp(request)\n } catch (error) {\n console.error('[record-email-state] the record system could not stamp the address', error)\n return null\n }\n}\n\n/**\n * Hands a send's delivery events to whichever plugin keeps the records, so\n * it can stamp the people who engaged (AGL-2616). Answers `null` when no\n * plugin keeps records, or the one that does keeps no engagement. Never\n * throws: the caller is a webhook whose acknowledgement must not hang on a\n * courtesy stamp.\n */\nexport async function stampRecordEmailEngagement(\n request: PluginRecordEmailEngagementRequest,\n): Promise<PluginRecordEmailStateReport | null> {\n const resolved = pluginRecordEmailStateWriter()\n if (!resolved?.writer.engaged) return null\n try {\n return await resolved.writer.engaged(request)\n } catch (error) {\n console.error('[record-email-state] the record system could not stamp an engagement', error)\n return null\n }\n}\n\n/**\n * Hands the addresses a marketing send was delivered to to whichever plugin\n * keeps the records (AGL-3446). Answers `null` when no plugin keeps\n * records, or the one that does keeps no such stage. Never throws: the\n * send has already delivered, and its bookkeeping must not fail it.\n */\nexport async function stampRecordEmailReach(\n request: PluginRecordEmailReachRequest,\n): Promise<PluginRecordEmailStateReport | null> {\n if (!request.emails.length) return null\n const resolved = pluginRecordEmailStateWriter()\n if (!resolved?.writer.reached) return null\n try {\n return await resolved.writer.reached(request)\n } catch (error) {\n console.error('[record-email-state] the record system could not note a delivered send', error)\n return null\n }\n}\n\n/**\n * Hands the addresses that wrote back to whichever plugin keeps the records\n * (AGL-3080). Answers `null` when no plugin keeps records, or the one that\n * does keeps no such stage. Never throws: the reply is already filed, and its\n * bookkeeping must not fail the sync that read it.\n */\nexport async function stampRecordEmailReply(\n request: PluginRecordEmailReplyRequest,\n): Promise<PluginRecordEmailStateReport | null> {\n if (!request.emails.length) return null\n const resolved = pluginRecordEmailStateWriter()\n if (!resolved?.writer.replied) return null\n try {\n return await resolved.writer.replied(request)\n } catch (error) {\n console.error('[record-email-state] the record system could not note a reply', error)\n return null\n }\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_EMAIL_STATE","multiple","registerPluginRecordEmailStateWriter","writer","options","pluginId","pluginRecordEmailStateWriter","entry","impl","stampRecordEmailState","request","resolved","stamp","error","console","stampRecordEmailEngagement","engaged","stampRecordEmailReach","emails","length","reached","stampRecordEmailReply","replied"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAuI1B,OAAO,MAAMC,4BAA4BH,4BACvC,2BACA;IAAEI,UAAU;AAAM,GACnB;AAED;;;;;CAKC,GACD,OAAO,SAASC,qCACdC,MAAoC,EACpCC,OAA+B;IAE/BN,sBAAsBE,2BAA2BG,QAAQ,aACnDC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAQA,6EAA6E,GAC7E,OAAO,SAASC;IACd,MAAMC,QAAQR,sBAAsBC,0BAA0B,CAAC,EAAE;IACjE,OAAOO,QAAQ;QAAEF,UAAUE,MAAMF,QAAQ;QAAEF,QAAQI,MAAMC,IAAI;IAAC,IAAI;AACpE;AAEA;;;;CAIC,GACD,OAAO,eAAeC,sBACpBC,OAAsC;IAEtC,MAAMC,WAAWL;IACjB,IAAI,CAACK,UAAU,OAAO;IACtB,IAAI;QACF,OAAO,MAAMA,SAASR,MAAM,CAACS,KAAK,CAACF;IACrC,EAAE,OAAOG,OAAO;QACdC,QAAQD,KAAK,CAAC,sEAAsEA;QACpF,OAAO;IACT;AACF;AAEA;;;;;;CAMC,GACD,OAAO,eAAeE,2BACpBL,OAA2C;IAE3C,MAAMC,WAAWL;IACjB,IAAI,EAACK,4BAAAA,SAAUR,MAAM,CAACa,OAAO,GAAE,OAAO;IACtC,IAAI;QACF,OAAO,MAAML,SAASR,MAAM,CAACa,OAAO,CAACN;IACvC,EAAE,OAAOG,OAAO;QACdC,QAAQD,KAAK,CAAC,wEAAwEA;QACtF,OAAO;IACT;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAeI,sBACpBP,OAAsC;IAEtC,IAAI,CAACA,QAAQQ,MAAM,CAACC,MAAM,EAAE,OAAO;IACnC,MAAMR,WAAWL;IACjB,IAAI,EAACK,4BAAAA,SAAUR,MAAM,CAACiB,OAAO,GAAE,OAAO;IACtC,IAAI;QACF,OAAO,MAAMT,SAASR,MAAM,CAACiB,OAAO,CAACV;IACvC,EAAE,OAAOG,OAAO;QACdC,QAAQD,KAAK,CAAC,0EAA0EA;QACxF,OAAO;IACT;AACF;AAEA;;;;;CAKC,GACD,OAAO,eAAeQ,sBACpBX,OAAsC;IAEtC,IAAI,CAACA,QAAQQ,MAAM,CAACC,MAAM,EAAE,OAAO;IACnC,MAAMR,WAAWL;IACjB,IAAI,EAACK,4BAAAA,SAAUR,MAAM,CAACmB,OAAO,GAAE,OAAO;IACtC,IAAI;QACF,OAAO,MAAMX,SAASR,MAAM,CAACmB,OAAO,CAACZ;IACvC,EAAE,OAAOG,OAAO;QACdC,QAAQD,KAAK,CAAC,iEAAiEA;QAC/E,OAAO;IACT;AACF"}
@@ -78,6 +78,16 @@ export interface PluginRecordIndex {
78
78
  get(request: PluginRecordIndexScope & {
79
79
  id: string;
80
80
  }): Promise<PluginIndexedRecord | null>;
81
+ /**
82
+ * One record's document — a Firestore Admin `DocumentReference` — for a
83
+ * reader that changes what the owner documents a reader may change, or
84
+ * keeps its own records under it; `null` for a scope the kind has none in.
85
+ * Whether the record exists is the reader's read to make. Optional: a kind
86
+ * whose owner offers no such thing is read through `get` alone.
87
+ */
88
+ ref?(request: PluginRecordIndexScope & {
89
+ id: string;
90
+ }): Promise<unknown>;
81
91
  }
82
92
  export declare const PLUGIN_RECORD_INDEXES: import("./plugin-services").PluginServiceContract<PluginRecordIndex>;
83
93
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-index.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 { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * A plugin's records, listed and read for ANOTHER plugin (AGL-3080).\n *\n * A plugin that works over records it does not own — an AI job enriching a\n * site's products, a composer binding the products an email names — used to\n * read the owner's Firestore collection itself: the path, the field names,\n * what \"deleted\" looks like. That is a plugin's storage reached around the\n * plugin, invisible to `check:lib-boundaries` because a collection name is\n * not an import, and it breaks the day the owner changes a field.\n *\n * So the owner publishes an INDEX for each record kind it keeps: `list` names\n * the live records of a scope, `get` reads one — each as an id, a name and the\n * FACTS the owner chooses to share, in a shape its registration documents.\n * The reader never learns where the records are stored or what a deleted one\n * looks like; the owner answers only live records.\n *\n * Its siblings: `plugin-record-cards` (how one record LOOKS, for a campaign\n * sender) and `plugin-record-facts` (what a record SAYS, for a summary);\n * `plugin-resource-drafts` is the WRITE side. This is the side that finds and\n * reads records to work on.\n *\n * ## Not registered is an answer\n *\n * No index for a kind means no plugin keeps it in this process — the owner is\n * not installed, or not loaded. A reader treats that as \"none here\" and says\n * so, rather than reaching for the collection itself. Registered from the\n * owner's server declarations, so every server process that boots the\n * declarations has it.\n *\n * ## One index per kind\n *\n * A second plugin registering an index for a kind another plugin already\n * keeps is refused, naming both, and the incumbent keeps serving.\n */\n\n/** One record, as its owner shares it. */\nexport interface PluginIndexedRecord {\n id: string\n /** What a person calls it. Never empty: the owner leaves an unnamed record out. */\n name: string\n /** What else the owner shares, in the shape its registration documents. */\n facts: Readonly<Record<string, unknown>>\n}\n\n/** Which records a reader asks about. */\nexport interface PluginRecordIndexScope {\n /** The organization the records belong to, where the kind is org-scoped. */\n orgId?: string | null\n /** The site the records belong to, where the kind is site-scoped. */\n hostId?: string | null\n}\n\nexport interface PluginRecordIndex {\n /**\n * The live records of the scope, at most `limit`, in the owner's order.\n * `truncated` says the scope holds more than were answered.\n */\n list(\n request: PluginRecordIndexScope & { limit: number },\n ): Promise<{ records: PluginIndexedRecord[]; truncated: boolean }>\n /** One live record, or `null` — gone, deleted, or not the scope's. */\n get(request: PluginRecordIndexScope & { id: string }): Promise<PluginIndexedRecord | null>\n}\n\nexport const PLUGIN_RECORD_INDEXES = definePluginServiceContract<PluginRecordIndex>(\n 'core.record-index',\n { multiple: true },\n)\n\n/**\n * Publishes the index for one record kind. The owner is the loader's marker\n * when a register fn is running, else `options.pluginId`; with neither the\n * registration throws. A kind another plugin keeps throws naming both.\n */\nexport function registerPluginRecordIndex(\n kind: string,\n index: PluginRecordIndex,\n options?: { pluginId?: string },\n): void {\n const key = kind.trim()\n if (!key) throw new Error('a record index needs a record kind')\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n if (!pluginId) {\n throw new Error(`the record index for \"${key}\" was registered with no owner`)\n }\n const incumbent = resolvePluginServices(PLUGIN_RECORD_INDEXES).find(\n (entry) => entry.key === key,\n )\n if (incumbent && incumbent.pluginId !== pluginId) {\n throw new Error(\n `record kind \"${key}\" is already indexed by \"${incumbent.pluginId}\"; refused \"${pluginId}\"`,\n )\n }\n registerPluginService(PLUGIN_RECORD_INDEXES, index, { pluginId, key })\n}\n\n/** The index for a kind, with the plugin that keeps it, or `null` when none does here. */\nexport function pluginRecordIndex(\n kind: string,\n): { pluginId: string; index: PluginRecordIndex } | null {\n const key = kind.trim()\n const entry = resolvePluginServices(PLUGIN_RECORD_INDEXES).find((one) => one.key === key)\n return entry ? { pluginId: entry.pluginId, index: entry.impl } : null\n}\n"],"names":["getRegisteringPluginId","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_INDEXES","multiple","registerPluginRecordIndex","kind","index","options","key","trim","Error","pluginId","incumbent","find","entry","pluginRecordIndex","one","impl"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,qCAAiC;AACxE,SACEC,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAkE1B,OAAO,MAAMC,wBAAwBH,4BACnC,qBACA;IAAEI,UAAU;AAAK,GAClB;AAED;;;;CAIC,GACD,OAAO,SAASC,0BACdC,IAAY,EACZC,KAAwB,EACxBC,OAA+B;QAIbT,MAAAA;IAFlB,MAAMU,MAAMH,KAAKI,IAAI;IACrB,IAAI,CAACD,KAAK,MAAM,IAAIE,MAAM;IAC1B,MAAMC,WAAW,EAACb,QAAAA,0BAAAA,oCAAAA,0BAA4BS,2BAAAA,QAASI,QAAQ,YAA7Cb,OAAiD,IAAIW,IAAI;IAC3E,IAAI,CAACE,UAAU;QACb,MAAM,IAAID,MAAM,CAAC,sBAAsB,EAAEF,IAAI,8BAA8B,CAAC;IAC9E;IACA,MAAMI,YAAYX,sBAAsBC,uBAAuBW,IAAI,CACjE,CAACC,QAAUA,MAAMN,GAAG,KAAKA;IAE3B,IAAII,aAAaA,UAAUD,QAAQ,KAAKA,UAAU;QAChD,MAAM,IAAID,MACR,CAAC,aAAa,EAAEF,IAAI,yBAAyB,EAAEI,UAAUD,QAAQ,CAAC,YAAY,EAAEA,SAAS,CAAC,CAAC;IAE/F;IACAX,sBAAsBE,uBAAuBI,OAAO;QAAEK;QAAUH;IAAI;AACtE;AAEA,wFAAwF,GACxF,OAAO,SAASO,kBACdV,IAAY;IAEZ,MAAMG,MAAMH,KAAKI,IAAI;IACrB,MAAMK,QAAQb,sBAAsBC,uBAAuBW,IAAI,CAAC,CAACG,MAAQA,IAAIR,GAAG,KAAKA;IACrF,OAAOM,QAAQ;QAAEH,UAAUG,MAAMH,QAAQ;QAAEL,OAAOQ,MAAMG,IAAI;IAAC,IAAI;AACnE"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-index.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 { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * A plugin's records, listed and read for ANOTHER plugin (AGL-3080).\n *\n * A plugin that works over records it does not own — an AI job enriching a\n * site's products, a composer binding the products an email names — used to\n * read the owner's Firestore collection itself: the path, the field names,\n * what \"deleted\" looks like. That is a plugin's storage reached around the\n * plugin, invisible to `check:lib-boundaries` because a collection name is\n * not an import, and it breaks the day the owner changes a field.\n *\n * So the owner publishes an INDEX for each record kind it keeps: `list` names\n * the live records of a scope, `get` reads one — each as an id, a name and the\n * FACTS the owner chooses to share, in a shape its registration documents.\n * The reader never learns where the records are stored or what a deleted one\n * looks like; the owner answers only live records.\n *\n * Its siblings: `plugin-record-cards` (how one record LOOKS, for a campaign\n * sender) and `plugin-record-facts` (what a record SAYS, for a summary);\n * `plugin-resource-drafts` is the WRITE side. This is the side that finds and\n * reads records to work on.\n *\n * ## Not registered is an answer\n *\n * No index for a kind means no plugin keeps it in this process — the owner is\n * not installed, or not loaded. A reader treats that as \"none here\" and says\n * so, rather than reaching for the collection itself. Registered from the\n * owner's server declarations, so every server process that boots the\n * declarations has it.\n *\n * ## One index per kind\n *\n * A second plugin registering an index for a kind another plugin already\n * keeps is refused, naming both, and the incumbent keeps serving.\n */\n\n/** One record, as its owner shares it. */\nexport interface PluginIndexedRecord {\n id: string\n /** What a person calls it. Never empty: the owner leaves an unnamed record out. */\n name: string\n /** What else the owner shares, in the shape its registration documents. */\n facts: Readonly<Record<string, unknown>>\n}\n\n/** Which records a reader asks about. */\nexport interface PluginRecordIndexScope {\n /** The organization the records belong to, where the kind is org-scoped. */\n orgId?: string | null\n /** The site the records belong to, where the kind is site-scoped. */\n hostId?: string | null\n}\n\nexport interface PluginRecordIndex {\n /**\n * The live records of the scope, at most `limit`, in the owner's order.\n * `truncated` says the scope holds more than were answered.\n */\n list(\n request: PluginRecordIndexScope & { limit: number },\n ): Promise<{ records: PluginIndexedRecord[]; truncated: boolean }>\n /** One live record, or `null` — gone, deleted, or not the scope's. */\n get(request: PluginRecordIndexScope & { id: string }): Promise<PluginIndexedRecord | null>\n /**\n * One record's document — a Firestore Admin `DocumentReference` — for a\n * reader that changes what the owner documents a reader may change, or\n * keeps its own records under it; `null` for a scope the kind has none in.\n * Whether the record exists is the reader's read to make. Optional: a kind\n * whose owner offers no such thing is read through `get` alone.\n */\n ref?(request: PluginRecordIndexScope & { id: string }): Promise<unknown>\n}\n\nexport const PLUGIN_RECORD_INDEXES = definePluginServiceContract<PluginRecordIndex>(\n 'core.record-index',\n { multiple: true },\n)\n\n/**\n * Publishes the index for one record kind. The owner is the loader's marker\n * when a register fn is running, else `options.pluginId`; with neither the\n * registration throws. A kind another plugin keeps throws naming both.\n */\nexport function registerPluginRecordIndex(\n kind: string,\n index: PluginRecordIndex,\n options?: { pluginId?: string },\n): void {\n const key = kind.trim()\n if (!key) throw new Error('a record index needs a record kind')\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n if (!pluginId) {\n throw new Error(`the record index for \"${key}\" was registered with no owner`)\n }\n const incumbent = resolvePluginServices(PLUGIN_RECORD_INDEXES).find(\n (entry) => entry.key === key,\n )\n if (incumbent && incumbent.pluginId !== pluginId) {\n throw new Error(\n `record kind \"${key}\" is already indexed by \"${incumbent.pluginId}\"; refused \"${pluginId}\"`,\n )\n }\n registerPluginService(PLUGIN_RECORD_INDEXES, index, { pluginId, key })\n}\n\n/** The index for a kind, with the plugin that keeps it, or `null` when none does here. */\nexport function pluginRecordIndex(\n kind: string,\n): { pluginId: string; index: PluginRecordIndex } | null {\n const key = kind.trim()\n const entry = resolvePluginServices(PLUGIN_RECORD_INDEXES).find((one) => one.key === key)\n return entry ? { pluginId: entry.pluginId, index: entry.impl } : null\n}\n"],"names":["getRegisteringPluginId","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_INDEXES","multiple","registerPluginRecordIndex","kind","index","options","key","trim","Error","pluginId","incumbent","find","entry","pluginRecordIndex","one","impl"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,qCAAiC;AACxE,SACEC,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AA0E1B,OAAO,MAAMC,wBAAwBH,4BACnC,qBACA;IAAEI,UAAU;AAAK,GAClB;AAED;;;;CAIC,GACD,OAAO,SAASC,0BACdC,IAAY,EACZC,KAAwB,EACxBC,OAA+B;QAIbT,MAAAA;IAFlB,MAAMU,MAAMH,KAAKI,IAAI;IACrB,IAAI,CAACD,KAAK,MAAM,IAAIE,MAAM;IAC1B,MAAMC,WAAW,EAACb,QAAAA,0BAAAA,oCAAAA,0BAA4BS,2BAAAA,QAASI,QAAQ,YAA7Cb,OAAiD,IAAIW,IAAI;IAC3E,IAAI,CAACE,UAAU;QACb,MAAM,IAAID,MAAM,CAAC,sBAAsB,EAAEF,IAAI,8BAA8B,CAAC;IAC9E;IACA,MAAMI,YAAYX,sBAAsBC,uBAAuBW,IAAI,CACjE,CAACC,QAAUA,MAAMN,GAAG,KAAKA;IAE3B,IAAII,aAAaA,UAAUD,QAAQ,KAAKA,UAAU;QAChD,MAAM,IAAID,MACR,CAAC,aAAa,EAAEF,IAAI,yBAAyB,EAAEI,UAAUD,QAAQ,CAAC,YAAY,EAAEA,SAAS,CAAC,CAAC;IAE/F;IACAX,sBAAsBE,uBAAuBI,OAAO;QAAEK;QAAUH;IAAI;AACtE;AAEA,wFAAwF,GACxF,OAAO,SAASO,kBACdV,IAAY;IAEZ,MAAMG,MAAMH,KAAKI,IAAI;IACrB,MAAMK,QAAQb,sBAAsBC,uBAAuBW,IAAI,CAAC,CAACG,MAAQA,IAAIR,GAAG,KAAKA;IACrF,OAAOM,QAAQ;QAAEH,UAAUG,MAAMH,QAAQ;QAAEL,OAAOQ,MAAMG,IAAI;IAAC,IAAI;AACnE"}
@@ -14,7 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import type { DocumentData, Firestore, Query } from 'firebase/firestore';
17
+ import type { DocumentData, DocumentReference, Firestore, Query } from 'firebase/firestore';
18
18
  import type { PluginIndexedRecord } from './plugin-record-index';
19
19
  /**
20
20
  * A plugin's records, LISTED in the console for another plugin's picker or
@@ -72,9 +72,38 @@ export interface PluginRecordListRequest {
72
72
  * owner keeps it. A kind that is never installed answers none.
73
73
  */
74
74
  installedFrom?: string | null;
75
+ /**
76
+ * The consent group the named site presents as (`app-utils/consent-groups`),
77
+ * for a kind whose records say different things to different groups — a
78
+ * person's name as one brand knows them. Absent reads as the site alone.
79
+ */
80
+ consentGroupId?: string | null;
81
+ /**
82
+ * The signed-in member, for a kind some of whose records are one member's
83
+ * own — a saved view kept private: the owner leaves out what this member
84
+ * may not list. Absent reads as nobody's, which lists only what is shared.
85
+ */
86
+ viewerUid?: string | null;
75
87
  /** The most documents the query may answer — a reader asks one past its window to learn it was cut. */
76
88
  limit: number;
77
89
  }
90
+ /** Where a reader walks a kind, or opens one record of it. */
91
+ export interface PluginRecordListScope {
92
+ /** The organization, where no site is named. */
93
+ orgId?: string | null;
94
+ /** The site; named, it wins over the organization. */
95
+ hostId?: string | null;
96
+ }
97
+ /**
98
+ * The most records one by-name read asks for: Firestore's `in` takes thirty
99
+ * values, so a reader with more asks in chunks.
100
+ */
101
+ export declare const PLUGIN_RECORD_LIST_IDS_MAX = 30;
102
+ /** Named records of a scope, for a reader that already holds their ids. */
103
+ export type PluginRecordListByIdsRequest = Omit<PluginRecordListRequest, 'limit' | 'search'> & {
104
+ /** At most {@link PLUGIN_RECORD_LIST_IDS_MAX}; the owner reads no more. */
105
+ ids: readonly string[];
106
+ };
78
107
  export interface PluginRecordListSource {
79
108
  /**
80
109
  * The query the signed-in member's read of the scope is proved by, at most
@@ -82,8 +111,38 @@ export interface PluginRecordListSource {
82
111
  * kind asked at the organization, a search with nothing to match).
83
112
  */
84
113
  query(firestore: Firestore, request: PluginRecordListRequest): Query<DocumentData> | null;
85
- /** One stored document as the owner shares it, or `null` to leave it out (deleted, unnamed). */
86
- record(id: string, data: Readonly<Record<string, unknown>>): PluginIndexedRecord | null;
114
+ /**
115
+ * The query for NAMED records of the scope — a reader that holds ids from
116
+ * somewhere else, an attribution naming the submission it credits — or
117
+ * `null` where none can be read. Optional: a source without it answers no
118
+ * by-name read, and the reader treats every id as one it could not find.
119
+ */
120
+ byIds?(firestore: Firestore, request: PluginRecordListByIdsRequest): Query<DocumentData> | null;
121
+ /**
122
+ * One stored document as the owner shares it, or `null` to leave it out
123
+ * (deleted, unnamed, or not the reader's to list). `request` is the one the
124
+ * query was built from, when the reader hands it back: a rule the query
125
+ * cannot state — a site's view of an org-wide row, a member's own view —
126
+ * is applied here.
127
+ */
128
+ record(id: string, data: Readonly<Record<string, unknown>>, request?: PluginRecordListRequest): PluginIndexedRecord | null;
129
+ /**
130
+ * The base a reader WALKS the kind from with the console's paged list query
131
+ * (`useListQuery`), which adds its filters, its order and its pages over the
132
+ * stored fields the owner documents for the kind — or `null` for a scope
133
+ * the kind has none in. For a kind whose reader pages a whole list rather
134
+ * than picking from a window. Where the rules admit a read only narrowed by
135
+ * a field, the owner's registration says which, and the reader adds it.
136
+ */
137
+ walk?(firestore: Firestore, scope: PluginRecordListScope): Query<DocumentData> | null;
138
+ /**
139
+ * One record's document, for a reader that opens it whole or changes what
140
+ * the owner documents a reader may change — the security rules hold the
141
+ * rest — or `null` for a scope the kind has none in.
142
+ */
143
+ doc?(firestore: Firestore, request: PluginRecordListScope & {
144
+ id: string;
145
+ }): DocumentReference<DocumentData> | null;
87
146
  }
88
147
  export declare const PLUGIN_RECORD_LISTS: import("./plugin-services").PluginServiceContract<PluginRecordListSource>;
89
148
  /**
@@ -106,10 +165,31 @@ export declare function pluginRecordListSource(kind: string): {
106
165
  * none.
107
166
  */
108
167
  export declare function pluginRecordListQuery(kind: string, firestore: Firestore, request: PluginRecordListRequest): Query<DocumentData> | null;
168
+ /**
169
+ * The base a reader walks `kind` from in a scope, or `null` — no plugin keeps
170
+ * the kind here, it offers no walk, or the scope has none.
171
+ */
172
+ export declare function pluginRecordListWalk(kind: string, firestore: Firestore, scope: PluginRecordListScope): Query<DocumentData> | null;
173
+ /**
174
+ * One record of `kind`'s document, or `null` — no plugin keeps the kind here,
175
+ * it offers no document, or the scope has none.
176
+ */
177
+ export declare function pluginRecordListDoc(kind: string, firestore: Firestore, request: PluginRecordListScope & {
178
+ id: string;
179
+ }): DocumentReference<DocumentData> | null;
180
+ /**
181
+ * The query for named records of `kind` — at most
182
+ * {@link PLUGIN_RECORD_LIST_IDS_MAX} ids, the rest left for the next chunk —
183
+ * or `null` when no plugin keeps the kind here, its source reads none by
184
+ * name, or there is no id to ask for.
185
+ */
186
+ export declare function pluginRecordListByIdsQuery(kind: string, firestore: Firestore, request: PluginRecordListByIdsRequest): Query<DocumentData> | null;
109
187
  /**
110
188
  * The documents a reader's listener answered for `kind`, as their owner
111
189
  * shares them: each row's id is read from `idField` (the listener's), and a
112
- * row the owner leaves out (deleted, unnamed) is not answered. None where no
113
- * plugin keeps the kind here.
190
+ * row the owner leaves out (deleted, unnamed, not this reader's) is not
191
+ * answered. None where no plugin keeps the kind here. `request` is the one
192
+ * the query was built from: hand it back, so the owner can apply what the
193
+ * query could not state.
114
194
  */
115
- export declare function pluginRecordsFromRows(kind: string, rows: ReadonlyArray<Readonly<Record<string, unknown>>> | null | undefined, idField?: string): PluginIndexedRecord[];
195
+ export declare function pluginRecordsFromRows(kind: string, rows: ReadonlyArray<Readonly<Record<string, unknown>>> | null | undefined, idField?: string, request?: PluginRecordListRequest): PluginIndexedRecord[];
@@ -13,8 +13,13 @@
13
13
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
- */ import { getRegisteringPluginId } from "../app-utils/registering-plugin.js";
16
+ */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
+ import { getRegisteringPluginId } from "../app-utils/registering-plugin.js";
17
18
  import { definePluginServiceContract, registerPluginService, resolvePluginServices } from "./plugin-services.js";
19
+ /**
20
+ * The most records one by-name read asks for: Firestore's `in` takes thirty
21
+ * values, so a reader with more asks in chunks.
22
+ */ export const PLUGIN_RECORD_LIST_IDS_MAX = 30;
18
23
  export const PLUGIN_RECORD_LISTS = definePluginServiceContract('core.record-lists', {
19
24
  multiple: true
20
25
  });
@@ -57,18 +62,50 @@ export const PLUGIN_RECORD_LISTS = definePluginServiceContract('core.record-list
57
62
  var _pluginRecordListSource;
58
63
  return (_ref = (_pluginRecordListSource = pluginRecordListSource(kind)) == null ? void 0 : _pluginRecordListSource.source.query(firestore, request)) != null ? _ref : null;
59
64
  }
65
+ /**
66
+ * The base a reader walks `kind` from in a scope, or `null` — no plugin keeps
67
+ * the kind here, it offers no walk, or the scope has none.
68
+ */ export function pluginRecordListWalk(kind, firestore, scope) {
69
+ var _ref;
70
+ var _pluginRecordListSource_source_walk, _pluginRecordListSource;
71
+ return (_ref = (_pluginRecordListSource = pluginRecordListSource(kind)) == null ? void 0 : (_pluginRecordListSource_source_walk = _pluginRecordListSource.source.walk) == null ? void 0 : _pluginRecordListSource_source_walk.call(_pluginRecordListSource.source, firestore, scope)) != null ? _ref : null;
72
+ }
73
+ /**
74
+ * One record of `kind`'s document, or `null` — no plugin keeps the kind here,
75
+ * it offers no document, or the scope has none.
76
+ */ export function pluginRecordListDoc(kind, firestore, request) {
77
+ var _ref;
78
+ var _pluginRecordListSource_source_doc, _pluginRecordListSource;
79
+ return (_ref = (_pluginRecordListSource = pluginRecordListSource(kind)) == null ? void 0 : (_pluginRecordListSource_source_doc = _pluginRecordListSource.source.doc) == null ? void 0 : _pluginRecordListSource_source_doc.call(_pluginRecordListSource.source, firestore, request)) != null ? _ref : null;
80
+ }
81
+ /**
82
+ * The query for named records of `kind` — at most
83
+ * {@link PLUGIN_RECORD_LIST_IDS_MAX} ids, the rest left for the next chunk —
84
+ * or `null` when no plugin keeps the kind here, its source reads none by
85
+ * name, or there is no id to ask for.
86
+ */ export function pluginRecordListByIdsQuery(kind, firestore, request) {
87
+ var _ref;
88
+ var _pluginRecordListSource_source_byIds, _pluginRecordListSource;
89
+ const ids = request.ids.filter(Boolean).slice(0, PLUGIN_RECORD_LIST_IDS_MAX);
90
+ if (!ids.length) return null;
91
+ return (_ref = (_pluginRecordListSource = pluginRecordListSource(kind)) == null ? void 0 : (_pluginRecordListSource_source_byIds = _pluginRecordListSource.source.byIds) == null ? void 0 : _pluginRecordListSource_source_byIds.call(_pluginRecordListSource.source, firestore, _extends({}, request, {
92
+ ids
93
+ }))) != null ? _ref : null;
94
+ }
60
95
  /**
61
96
  * The documents a reader's listener answered for `kind`, as their owner
62
97
  * shares them: each row's id is read from `idField` (the listener's), and a
63
- * row the owner leaves out (deleted, unnamed) is not answered. None where no
64
- * plugin keeps the kind here.
65
- */ export function pluginRecordsFromRows(kind, rows, idField = '$id') {
98
+ * row the owner leaves out (deleted, unnamed, not this reader's) is not
99
+ * answered. None where no plugin keeps the kind here. `request` is the one
100
+ * the query was built from: hand it back, so the owner can apply what the
101
+ * query could not state.
102
+ */ export function pluginRecordsFromRows(kind, rows, idField = '$id', request) {
66
103
  var _pluginRecordListSource;
67
104
  const source = (_pluginRecordListSource = pluginRecordListSource(kind)) == null ? void 0 : _pluginRecordListSource.source;
68
105
  if (!source) return [];
69
106
  return (rows != null ? rows : []).map((row)=>{
70
107
  var _row_idField;
71
- return source.record(String((_row_idField = row[idField]) != null ? _row_idField : ''), row);
108
+ return source.record(String((_row_idField = row[idField]) != null ? _row_idField : ''), row, request);
72
109
  }).filter((record)=>record !== null);
73
110
  }
74
111
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-lists.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 { DocumentData, Firestore, Query } from 'firebase/firestore'\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport type { PluginIndexedRecord } from './plugin-record-index'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * A plugin's records, LISTED in the console for another plugin's picker or\n * check (AGL-3080) — the browser's half of `plugin-record-index`.\n *\n * A plugin that offers records it does not own — an automation step picking\n * a dataset, a reference check confirming a workflow still exists, a deal\n * picking a product — used to query the owner's collection from the browser\n * itself: the path, the filter the owner's security rules require, the field\n * a name is in, what a deleted one looks like. Each breaks the day the owner\n * changes one, and none is visible to `check:lib-boundaries`.\n *\n * So the owner publishes a LIST SOURCE for each record kind it keeps: the\n * query the signed-in member's read of a scope is proved by, and how one\n * stored document reads as a record — an id, a name and the facts the owner\n * chooses to share, in the shape its registration documents, the same shape\n * its server index answers. The reader runs the query with its own listener\n * (`pluginRecordListQuery`) and reads the rows back through the owner\n * (`pluginRecordsFromRows`), and never learns where the records are stored.\n *\n * ## Not registered is an answer\n *\n * No source for a kind means no plugin keeps it in this console — the owner is\n * not installed or not loaded. A reader treats that as \"none here\" and says\n * so, rather than reaching for the collection itself.\n *\n * ## One source per kind\n *\n * A second plugin registering a source for a kind another plugin already\n * keeps is refused, naming both, and the incumbent keeps serving.\n */\n\n/** Which records a reader lists. */\nexport interface PluginRecordListRequest {\n /** The organization the records belong to, where the kind is org-scoped. */\n orgId?: string | null\n /**\n * The site the records are listed for. An org-scoped kind narrows to what\n * the site may use; `null` lists the organization's own view, which only an\n * organization-wide reader may hold.\n */\n hostId?: string | null\n /** What a person typed, matched the owner's way; absent lists the scope. */\n search?: string | null\n /**\n * The signed-in member's own scope tokens, where they are not\n * organization-wide and no site is named: an org-scoped kind narrows to\n * what they may see, which is the filter the security rules require of\n * them. Absent for an organization-wide reader; a named site's narrowing\n * wins over it, and a site's own kind has no use for it.\n */\n memberScope?: readonly string[] | null\n /**\n * Only the records installed from this listing — the `listingId` of their\n * install stamp (`app-utils/artifact-provenance.ts`), matched wherever the\n * owner keeps it. A kind that is never installed answers none.\n */\n installedFrom?: string | null\n /** The most documents the query may answer — a reader asks one past its window to learn it was cut. */\n limit: number\n}\n\nexport interface PluginRecordListSource {\n /**\n * The query the signed-in member's read of the scope is proved by, at most\n * `limit` documents, or `null` for a scope the kind has none in (a site-only\n * kind asked at the organization, a search with nothing to match).\n */\n query(firestore: Firestore, request: PluginRecordListRequest): Query<DocumentData> | null\n /** One stored document as the owner shares it, or `null` to leave it out (deleted, unnamed). */\n record(id: string, data: Readonly<Record<string, unknown>>): PluginIndexedRecord | null\n}\n\nexport const PLUGIN_RECORD_LISTS = definePluginServiceContract<PluginRecordListSource>(\n 'core.record-lists',\n { multiple: true },\n)\n\n/**\n * Publishes the list source for one record kind. The owner is the loader's\n * marker when a register fn is running, else `options.pluginId`; with neither\n * the registration throws. A kind another plugin lists throws naming both.\n */\nexport function registerPluginRecordListSource(\n kind: string,\n source: PluginRecordListSource,\n options?: { pluginId?: string },\n): void {\n const key = kind.trim()\n if (!key) throw new Error('a record list source needs a record kind')\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n if (!pluginId) {\n throw new Error(`the record list source for \"${key}\" was registered with no owner`)\n }\n const incumbent = resolvePluginServices(PLUGIN_RECORD_LISTS).find((entry) => entry.key === key)\n if (incumbent && incumbent.pluginId !== pluginId) {\n throw new Error(\n `record kind \"${key}\" is already listed by \"${incumbent.pluginId}\"; refused \"${pluginId}\"`,\n )\n }\n registerPluginService(PLUGIN_RECORD_LISTS, source, { pluginId, key })\n}\n\n/** The list source for a kind, with the plugin that keeps it, or `null` when none does here. */\nexport function pluginRecordListSource(\n kind: string,\n): { pluginId: string; source: PluginRecordListSource } | null {\n const key = kind.trim()\n const entry = resolvePluginServices(PLUGIN_RECORD_LISTS).find((one) => one.key === key)\n return entry ? { pluginId: entry.pluginId, source: entry.impl } : null\n}\n\n/**\n * The query a reader listens to for a kind's records — with the console's own\n * collection listener, so it is bounded, retried and reported like every\n * other list — or `null` when no plugin keeps the kind here or the scope has\n * none.\n */\nexport function pluginRecordListQuery(\n kind: string,\n firestore: Firestore,\n request: PluginRecordListRequest,\n): Query<DocumentData> | null {\n return pluginRecordListSource(kind)?.source.query(firestore, request) ?? null\n}\n\n/**\n * The documents a reader's listener answered for `kind`, as their owner\n * shares them: each row's id is read from `idField` (the listener's), and a\n * row the owner leaves out (deleted, unnamed) is not answered. None where no\n * plugin keeps the kind here.\n */\nexport function pluginRecordsFromRows(\n kind: string,\n rows: ReadonlyArray<Readonly<Record<string, unknown>>> | null | undefined,\n idField = '$id',\n): PluginIndexedRecord[] {\n const source = pluginRecordListSource(kind)?.source\n if (!source) return []\n return (rows ?? [])\n .map((row) => source.record(String(row[idField] ?? ''), row))\n .filter((record): record is PluginIndexedRecord => record !== null)\n}\n"],"names":["getRegisteringPluginId","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_LISTS","multiple","registerPluginRecordListSource","kind","source","options","key","trim","Error","pluginId","incumbent","find","entry","pluginRecordListSource","one","impl","pluginRecordListQuery","firestore","request","query","pluginRecordsFromRows","rows","idField","map","row","record","String","filter"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,sBAAsB,QAAQ,qCAAiC;AAExE,SACEC,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AA0E1B,OAAO,MAAMC,sBAAsBH,4BACjC,qBACA;IAAEI,UAAU;AAAK,GAClB;AAED;;;;CAIC,GACD,OAAO,SAASC,+BACdC,IAAY,EACZC,MAA8B,EAC9BC,OAA+B;QAIbT,MAAAA;IAFlB,MAAMU,MAAMH,KAAKI,IAAI;IACrB,IAAI,CAACD,KAAK,MAAM,IAAIE,MAAM;IAC1B,MAAMC,WAAW,EAACb,QAAAA,0BAAAA,oCAAAA,0BAA4BS,2BAAAA,QAASI,QAAQ,YAA7Cb,OAAiD,IAAIW,IAAI;IAC3E,IAAI,CAACE,UAAU;QACb,MAAM,IAAID,MAAM,CAAC,4BAA4B,EAAEF,IAAI,8BAA8B,CAAC;IACpF;IACA,MAAMI,YAAYX,sBAAsBC,qBAAqBW,IAAI,CAAC,CAACC,QAAUA,MAAMN,GAAG,KAAKA;IAC3F,IAAII,aAAaA,UAAUD,QAAQ,KAAKA,UAAU;QAChD,MAAM,IAAID,MACR,CAAC,aAAa,EAAEF,IAAI,wBAAwB,EAAEI,UAAUD,QAAQ,CAAC,YAAY,EAAEA,SAAS,CAAC,CAAC;IAE9F;IACAX,sBAAsBE,qBAAqBI,QAAQ;QAAEK;QAAUH;IAAI;AACrE;AAEA,8FAA8F,GAC9F,OAAO,SAASO,uBACdV,IAAY;IAEZ,MAAMG,MAAMH,KAAKI,IAAI;IACrB,MAAMK,QAAQb,sBAAsBC,qBAAqBW,IAAI,CAAC,CAACG,MAAQA,IAAIR,GAAG,KAAKA;IACnF,OAAOM,QAAQ;QAAEH,UAAUG,MAAMH,QAAQ;QAAEL,QAAQQ,MAAMG,IAAI;IAAC,IAAI;AACpE;AAEA;;;;;CAKC,GACD,OAAO,SAASC,sBACdb,IAAY,EACZc,SAAoB,EACpBC,OAAgC;;QAEzBL;IAAP,gBAAOA,0BAAAA,uBAAuBV,0BAAvBU,wBAA8BT,MAAM,CAACe,KAAK,CAACF,WAAWC,2BAAY;AAC3E;AAEA;;;;;CAKC,GACD,OAAO,SAASE,sBACdjB,IAAY,EACZkB,IAAyE,EACzEC,UAAU,KAAK;QAEAT;IAAf,MAAMT,UAASS,0BAAAA,uBAAuBV,0BAAvBU,wBAA8BT,MAAM;IACnD,IAAI,CAACA,QAAQ,OAAO,EAAE;IACtB,OAAO,CAACiB,eAAAA,OAAQ,EAAE,EACfE,GAAG,CAAC,CAACC;YAA6BA;eAArBpB,OAAOqB,MAAM,CAACC,QAAOF,eAAAA,GAAG,CAACF,QAAQ,YAAZE,eAAgB,KAAKA;OACvDG,MAAM,CAAC,CAACF,SAA0CA,WAAW;AAClE"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-lists.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 { DocumentData, DocumentReference, Firestore, Query } from 'firebase/firestore'\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport type { PluginIndexedRecord } from './plugin-record-index'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * A plugin's records, LISTED in the console for another plugin's picker or\n * check (AGL-3080) — the browser's half of `plugin-record-index`.\n *\n * A plugin that offers records it does not own — an automation step picking\n * a dataset, a reference check confirming a workflow still exists, a deal\n * picking a product — used to query the owner's collection from the browser\n * itself: the path, the filter the owner's security rules require, the field\n * a name is in, what a deleted one looks like. Each breaks the day the owner\n * changes one, and none is visible to `check:lib-boundaries`.\n *\n * So the owner publishes a LIST SOURCE for each record kind it keeps: the\n * query the signed-in member's read of a scope is proved by, and how one\n * stored document reads as a record — an id, a name and the facts the owner\n * chooses to share, in the shape its registration documents, the same shape\n * its server index answers. The reader runs the query with its own listener\n * (`pluginRecordListQuery`) and reads the rows back through the owner\n * (`pluginRecordsFromRows`), and never learns where the records are stored.\n *\n * ## Not registered is an answer\n *\n * No source for a kind means no plugin keeps it in this console — the owner is\n * not installed or not loaded. A reader treats that as \"none here\" and says\n * so, rather than reaching for the collection itself.\n *\n * ## One source per kind\n *\n * A second plugin registering a source for a kind another plugin already\n * keeps is refused, naming both, and the incumbent keeps serving.\n */\n\n/** Which records a reader lists. */\nexport interface PluginRecordListRequest {\n /** The organization the records belong to, where the kind is org-scoped. */\n orgId?: string | null\n /**\n * The site the records are listed for. An org-scoped kind narrows to what\n * the site may use; `null` lists the organization's own view, which only an\n * organization-wide reader may hold.\n */\n hostId?: string | null\n /** What a person typed, matched the owner's way; absent lists the scope. */\n search?: string | null\n /**\n * The signed-in member's own scope tokens, where they are not\n * organization-wide and no site is named: an org-scoped kind narrows to\n * what they may see, which is the filter the security rules require of\n * them. Absent for an organization-wide reader; a named site's narrowing\n * wins over it, and a site's own kind has no use for it.\n */\n memberScope?: readonly string[] | null\n /**\n * Only the records installed from this listing — the `listingId` of their\n * install stamp (`app-utils/artifact-provenance.ts`), matched wherever the\n * owner keeps it. A kind that is never installed answers none.\n */\n installedFrom?: string | null\n /**\n * The consent group the named site presents as (`app-utils/consent-groups`),\n * for a kind whose records say different things to different groups — a\n * person's name as one brand knows them. Absent reads as the site alone.\n */\n consentGroupId?: string | null\n /**\n * The signed-in member, for a kind some of whose records are one member's\n * own — a saved view kept private: the owner leaves out what this member\n * may not list. Absent reads as nobody's, which lists only what is shared.\n */\n viewerUid?: string | null\n /** The most documents the query may answer — a reader asks one past its window to learn it was cut. */\n limit: number\n}\n\n/** Where a reader walks a kind, or opens one record of it. */\nexport interface PluginRecordListScope {\n /** The organization, where no site is named. */\n orgId?: string | null\n /** The site; named, it wins over the organization. */\n hostId?: string | null\n}\n\n/**\n * The most records one by-name read asks for: Firestore's `in` takes thirty\n * values, so a reader with more asks in chunks.\n */\nexport const PLUGIN_RECORD_LIST_IDS_MAX = 30\n\n/** Named records of a scope, for a reader that already holds their ids. */\nexport type PluginRecordListByIdsRequest = Omit<PluginRecordListRequest, 'limit' | 'search'> & {\n /** At most {@link PLUGIN_RECORD_LIST_IDS_MAX}; the owner reads no more. */\n ids: readonly string[]\n}\n\nexport interface PluginRecordListSource {\n /**\n * The query the signed-in member's read of the scope is proved by, at most\n * `limit` documents, or `null` for a scope the kind has none in (a site-only\n * kind asked at the organization, a search with nothing to match).\n */\n query(firestore: Firestore, request: PluginRecordListRequest): Query<DocumentData> | null\n /**\n * The query for NAMED records of the scope — a reader that holds ids from\n * somewhere else, an attribution naming the submission it credits — or\n * `null` where none can be read. Optional: a source without it answers no\n * by-name read, and the reader treats every id as one it could not find.\n */\n byIds?(firestore: Firestore, request: PluginRecordListByIdsRequest): Query<DocumentData> | null\n /**\n * One stored document as the owner shares it, or `null` to leave it out\n * (deleted, unnamed, or not the reader's to list). `request` is the one the\n * query was built from, when the reader hands it back: a rule the query\n * cannot state — a site's view of an org-wide row, a member's own view —\n * is applied here.\n */\n record(\n id: string,\n data: Readonly<Record<string, unknown>>,\n request?: PluginRecordListRequest,\n ): PluginIndexedRecord | null\n /**\n * The base a reader WALKS the kind from with the console's paged list query\n * (`useListQuery`), which adds its filters, its order and its pages over the\n * stored fields the owner documents for the kind — or `null` for a scope\n * the kind has none in. For a kind whose reader pages a whole list rather\n * than picking from a window. Where the rules admit a read only narrowed by\n * a field, the owner's registration says which, and the reader adds it.\n */\n walk?(firestore: Firestore, scope: PluginRecordListScope): Query<DocumentData> | null\n /**\n * One record's document, for a reader that opens it whole or changes what\n * the owner documents a reader may change — the security rules hold the\n * rest — or `null` for a scope the kind has none in.\n */\n doc?(\n firestore: Firestore,\n request: PluginRecordListScope & { id: string },\n ): DocumentReference<DocumentData> | null\n}\n\nexport const PLUGIN_RECORD_LISTS = definePluginServiceContract<PluginRecordListSource>(\n 'core.record-lists',\n { multiple: true },\n)\n\n/**\n * Publishes the list source for one record kind. The owner is the loader's\n * marker when a register fn is running, else `options.pluginId`; with neither\n * the registration throws. A kind another plugin lists throws naming both.\n */\nexport function registerPluginRecordListSource(\n kind: string,\n source: PluginRecordListSource,\n options?: { pluginId?: string },\n): void {\n const key = kind.trim()\n if (!key) throw new Error('a record list source needs a record kind')\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n if (!pluginId) {\n throw new Error(`the record list source for \"${key}\" was registered with no owner`)\n }\n const incumbent = resolvePluginServices(PLUGIN_RECORD_LISTS).find((entry) => entry.key === key)\n if (incumbent && incumbent.pluginId !== pluginId) {\n throw new Error(\n `record kind \"${key}\" is already listed by \"${incumbent.pluginId}\"; refused \"${pluginId}\"`,\n )\n }\n registerPluginService(PLUGIN_RECORD_LISTS, source, { pluginId, key })\n}\n\n/** The list source for a kind, with the plugin that keeps it, or `null` when none does here. */\nexport function pluginRecordListSource(\n kind: string,\n): { pluginId: string; source: PluginRecordListSource } | null {\n const key = kind.trim()\n const entry = resolvePluginServices(PLUGIN_RECORD_LISTS).find((one) => one.key === key)\n return entry ? { pluginId: entry.pluginId, source: entry.impl } : null\n}\n\n/**\n * The query a reader listens to for a kind's records — with the console's own\n * collection listener, so it is bounded, retried and reported like every\n * other list — or `null` when no plugin keeps the kind here or the scope has\n * none.\n */\nexport function pluginRecordListQuery(\n kind: string,\n firestore: Firestore,\n request: PluginRecordListRequest,\n): Query<DocumentData> | null {\n return pluginRecordListSource(kind)?.source.query(firestore, request) ?? null\n}\n\n/**\n * The base a reader walks `kind` from in a scope, or `null` — no plugin keeps\n * the kind here, it offers no walk, or the scope has none.\n */\nexport function pluginRecordListWalk(\n kind: string,\n firestore: Firestore,\n scope: PluginRecordListScope,\n): Query<DocumentData> | null {\n return pluginRecordListSource(kind)?.source.walk?.(firestore, scope) ?? null\n}\n\n/**\n * One record of `kind`'s document, or `null` — no plugin keeps the kind here,\n * it offers no document, or the scope has none.\n */\nexport function pluginRecordListDoc(\n kind: string,\n firestore: Firestore,\n request: PluginRecordListScope & { id: string },\n): DocumentReference<DocumentData> | null {\n return pluginRecordListSource(kind)?.source.doc?.(firestore, request) ?? null\n}\n\n/**\n * The query for named records of `kind` — at most\n * {@link PLUGIN_RECORD_LIST_IDS_MAX} ids, the rest left for the next chunk —\n * or `null` when no plugin keeps the kind here, its source reads none by\n * name, or there is no id to ask for.\n */\nexport function pluginRecordListByIdsQuery(\n kind: string,\n firestore: Firestore,\n request: PluginRecordListByIdsRequest,\n): Query<DocumentData> | null {\n const ids = request.ids.filter(Boolean).slice(0, PLUGIN_RECORD_LIST_IDS_MAX)\n if (!ids.length) return null\n return pluginRecordListSource(kind)?.source.byIds?.(firestore, { ...request, ids }) ?? null\n}\n\n/**\n * The documents a reader's listener answered for `kind`, as their owner\n * shares them: each row's id is read from `idField` (the listener's), and a\n * row the owner leaves out (deleted, unnamed, not this reader's) is not\n * answered. None where no plugin keeps the kind here. `request` is the one\n * the query was built from: hand it back, so the owner can apply what the\n * query could not state.\n */\nexport function pluginRecordsFromRows(\n kind: string,\n rows: ReadonlyArray<Readonly<Record<string, unknown>>> | null | undefined,\n idField = '$id',\n request?: PluginRecordListRequest,\n): PluginIndexedRecord[] {\n const source = pluginRecordListSource(kind)?.source\n if (!source) return []\n return (rows ?? [])\n .map((row) => source.record(String(row[idField] ?? ''), row, request))\n .filter((record): record is PluginIndexedRecord => record !== null)\n}\n"],"names":["getRegisteringPluginId","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_LIST_IDS_MAX","PLUGIN_RECORD_LISTS","multiple","registerPluginRecordListSource","kind","source","options","key","trim","Error","pluginId","incumbent","find","entry","pluginRecordListSource","one","impl","pluginRecordListQuery","firestore","request","query","pluginRecordListWalk","scope","walk","pluginRecordListDoc","doc","pluginRecordListByIdsQuery","ids","filter","Boolean","slice","length","byIds","pluginRecordsFromRows","rows","idField","map","row","record","String"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,sBAAsB,QAAQ,qCAAiC;AAExE,SACEC,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAmF1B;;;CAGC,GACD,OAAO,MAAMC,6BAA6B,GAAE;AAsD5C,OAAO,MAAMC,sBAAsBJ,4BACjC,qBACA;IAAEK,UAAU;AAAK,GAClB;AAED;;;;CAIC,GACD,OAAO,SAASC,+BACdC,IAAY,EACZC,MAA8B,EAC9BC,OAA+B;QAIbV,MAAAA;IAFlB,MAAMW,MAAMH,KAAKI,IAAI;IACrB,IAAI,CAACD,KAAK,MAAM,IAAIE,MAAM;IAC1B,MAAMC,WAAW,EAACd,QAAAA,0BAAAA,oCAAAA,0BAA4BU,2BAAAA,QAASI,QAAQ,YAA7Cd,OAAiD,IAAIY,IAAI;IAC3E,IAAI,CAACE,UAAU;QACb,MAAM,IAAID,MAAM,CAAC,4BAA4B,EAAEF,IAAI,8BAA8B,CAAC;IACpF;IACA,MAAMI,YAAYZ,sBAAsBE,qBAAqBW,IAAI,CAAC,CAACC,QAAUA,MAAMN,GAAG,KAAKA;IAC3F,IAAII,aAAaA,UAAUD,QAAQ,KAAKA,UAAU;QAChD,MAAM,IAAID,MACR,CAAC,aAAa,EAAEF,IAAI,wBAAwB,EAAEI,UAAUD,QAAQ,CAAC,YAAY,EAAEA,SAAS,CAAC,CAAC;IAE9F;IACAZ,sBAAsBG,qBAAqBI,QAAQ;QAAEK;QAAUH;IAAI;AACrE;AAEA,8FAA8F,GAC9F,OAAO,SAASO,uBACdV,IAAY;IAEZ,MAAMG,MAAMH,KAAKI,IAAI;IACrB,MAAMK,QAAQd,sBAAsBE,qBAAqBW,IAAI,CAAC,CAACG,MAAQA,IAAIR,GAAG,KAAKA;IACnF,OAAOM,QAAQ;QAAEH,UAAUG,MAAMH,QAAQ;QAAEL,QAAQQ,MAAMG,IAAI;IAAC,IAAI;AACpE;AAEA;;;;;CAKC,GACD,OAAO,SAASC,sBACdb,IAAY,EACZc,SAAoB,EACpBC,OAAgC;;QAEzBL;IAAP,gBAAOA,0BAAAA,uBAAuBV,0BAAvBU,wBAA8BT,MAAM,CAACe,KAAK,CAACF,WAAWC,2BAAY;AAC3E;AAEA;;;CAGC,GACD,OAAO,SAASE,qBACdjB,IAAY,EACZc,SAAoB,EACpBI,KAA4B;;QAErBR,qCAAAA;IAAP,gBAAOA,0BAAAA,uBAAuBV,2BAAvBU,sCAAAA,wBAA8BT,MAAM,CAACkB,IAAI,qBAAzCT,yCAAAA,wBAA8BT,MAAM,EAAQa,WAAWI,yBAAU;AAC1E;AAEA;;;CAGC,GACD,OAAO,SAASE,oBACdpB,IAAY,EACZc,SAAoB,EACpBC,OAA+C;;QAExCL,oCAAAA;IAAP,gBAAOA,0BAAAA,uBAAuBV,2BAAvBU,qCAAAA,wBAA8BT,MAAM,CAACoB,GAAG,qBAAxCX,wCAAAA,wBAA8BT,MAAM,EAAOa,WAAWC,2BAAY;AAC3E;AAEA;;;;;CAKC,GACD,OAAO,SAASO,2BACdtB,IAAY,EACZc,SAAoB,EACpBC,OAAqC;;QAI9BL,sCAAAA;IAFP,MAAMa,MAAMR,QAAQQ,GAAG,CAACC,MAAM,CAACC,SAASC,KAAK,CAAC,GAAG9B;IACjD,IAAI,CAAC2B,IAAII,MAAM,EAAE,OAAO;IACxB,gBAAOjB,0BAAAA,uBAAuBV,2BAAvBU,uCAAAA,wBAA8BT,MAAM,CAAC2B,KAAK,qBAA1ClB,0CAAAA,wBAA8BT,MAAM,EAASa,WAAW,aAAKC;QAASQ;0BAAU;AACzF;AAEA;;;;;;;CAOC,GACD,OAAO,SAASM,sBACd7B,IAAY,EACZ8B,IAAyE,EACzEC,UAAU,KAAK,EACfhB,OAAiC;QAElBL;IAAf,MAAMT,UAASS,0BAAAA,uBAAuBV,0BAAvBU,wBAA8BT,MAAM;IACnD,IAAI,CAACA,QAAQ,OAAO,EAAE;IACtB,OAAO,CAAC6B,eAAAA,OAAQ,EAAE,EACfE,GAAG,CAAC,CAACC;YAA6BA;eAArBhC,OAAOiC,MAAM,CAACC,QAAOF,eAAAA,GAAG,CAACF,QAAQ,YAAZE,eAAgB,KAAKA,KAAKlB;OAC5DS,MAAM,CAAC,CAACU,SAA0CA,WAAW;AAClE"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * WHERE A PERSON FIRST CAME FROM, told to the record system (AGL-3519).
19
+ *
20
+ * A door that meets somebody — the platform's own account sign-up, a sales
21
+ * sequence enrolling them — knows HOW it met them, and the record system
22
+ * keeps the field a sales team reads that answer from. The door must not
23
+ * write that plugin's records itself, nor import it; so the record system
24
+ * registers a WRITER here, and the door names the person and its own word
25
+ * for how it met them.
26
+ *
27
+ * ## Original source: never overwritten
28
+ *
29
+ * The writer records the origin only on a record that holds none — the
30
+ * first door to meet a person names where they came from, and nothing later
31
+ * moves it. What the word becomes, and whether the workspace still offers
32
+ * it, is the writer's to decide; a word it does not know records nothing.
33
+ *
34
+ * A single-implementation contract: a workspace keeps one record system.
35
+ */
36
+ /**
37
+ * A first-party door's word for how it met a person. Open, so a plugin's own
38
+ * door can name itself; the record system ignores a word it has no value for.
39
+ */
40
+ export type PluginRecordOrigin = 'form' | 'booking' | 'newsletter' | 'member' | 'order' | 'account' | 'sequence' | 'emailCampaign' | (string & {});
41
+ export interface PluginRecordOriginRequest {
42
+ /** The organization whose records are stamped, when the caller knows it. */
43
+ orgId?: string;
44
+ /** The site the door met the person on: whose records, and whose view of them. */
45
+ hostId: string;
46
+ email: string;
47
+ origin: PluginRecordOrigin;
48
+ /**
49
+ * Only a record this door just started — a person met for the first time.
50
+ * A door that touches people already on file (a capture on a known
51
+ * address) asks for this; one acting on a person deliberately (an
52
+ * enrollment) does not.
53
+ */
54
+ firstTouchOnly?: boolean;
55
+ }
56
+ export interface PluginRecordOriginReport {
57
+ /** Records that took the origin. */
58
+ records: number;
59
+ }
60
+ export interface PluginRecordOriginWriter {
61
+ /** Never throws: the door has already done what it records. */
62
+ stamp(request: PluginRecordOriginRequest): Promise<PluginRecordOriginReport>;
63
+ }
64
+ export declare const PLUGIN_RECORD_ORIGIN: import("./plugin-services").PluginServiceContract<PluginRecordOriginWriter>;
65
+ /**
66
+ * Registers the workspace's record-origin writer. The owner is the loader's
67
+ * marker when a register fn is running, else `options.pluginId`; with
68
+ * neither the registration throws, and a second plugin's writer is refused
69
+ * naming both — the incumbent keeps serving.
70
+ */
71
+ export declare function registerPluginRecordOriginWriter(writer: PluginRecordOriginWriter, options?: {
72
+ pluginId?: string;
73
+ }): void;
74
+ /**
75
+ * Records where a person came from through whichever plugin keeps the
76
+ * records, or answers `null` when none does. Never throws: every caller has
77
+ * already met the person, and the origin is bookkeeping beside it.
78
+ */
79
+ export declare function stampRecordOrigin(request: PluginRecordOriginRequest): Promise<PluginRecordOriginReport | null>;
@@ -0,0 +1,46 @@
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
+ */ import { definePluginServiceContract, registerPluginService, resolvePluginServices } from "./plugin-services.js";
18
+ export const PLUGIN_RECORD_ORIGIN = definePluginServiceContract('core.record-origin', {
19
+ multiple: false
20
+ });
21
+ /**
22
+ * Registers the workspace's record-origin writer. The owner is the loader's
23
+ * marker when a register fn is running, else `options.pluginId`; with
24
+ * neither the registration throws, and a second plugin's writer is refused
25
+ * naming both — the incumbent keeps serving.
26
+ */ export function registerPluginRecordOriginWriter(writer, options) {
27
+ registerPluginService(PLUGIN_RECORD_ORIGIN, writer, _extends({}, (options == null ? void 0 : options.pluginId) ? {
28
+ pluginId: options.pluginId
29
+ } : {}));
30
+ }
31
+ /**
32
+ * Records where a person came from through whichever plugin keeps the
33
+ * records, or answers `null` when none does. Never throws: every caller has
34
+ * already met the person, and the origin is bookkeeping beside it.
35
+ */ export async function stampRecordOrigin(request) {
36
+ const entry = resolvePluginServices(PLUGIN_RECORD_ORIGIN)[0];
37
+ if (!entry) return null;
38
+ try {
39
+ return await entry.impl.stamp(request);
40
+ } catch (error) {
41
+ console.error('[record-origin] the record system could not record where a person came from', error);
42
+ return null;
43
+ }
44
+ }
45
+
46
+ //# sourceMappingURL=plugin-record-origin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-origin.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 {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * WHERE A PERSON FIRST CAME FROM, told to the record system (AGL-3519).\n *\n * A door that meets somebody — the platform's own account sign-up, a sales\n * sequence enrolling them — knows HOW it met them, and the record system\n * keeps the field a sales team reads that answer from. The door must not\n * write that plugin's records itself, nor import it; so the record system\n * registers a WRITER here, and the door names the person and its own word\n * for how it met them.\n *\n * ## Original source: never overwritten\n *\n * The writer records the origin only on a record that holds none — the\n * first door to meet a person names where they came from, and nothing later\n * moves it. What the word becomes, and whether the workspace still offers\n * it, is the writer's to decide; a word it does not know records nothing.\n *\n * A single-implementation contract: a workspace keeps one record system.\n */\n\n/**\n * A first-party door's word for how it met a person. Open, so a plugin's own\n * door can name itself; the record system ignores a word it has no value for.\n */\nexport type PluginRecordOrigin =\n | 'form'\n | 'booking'\n | 'newsletter'\n | 'member'\n | 'order'\n | 'account'\n | 'sequence'\n | 'emailCampaign'\n | (string & {})\n\nexport interface PluginRecordOriginRequest {\n /** The organization whose records are stamped, when the caller knows it. */\n orgId?: string\n /** The site the door met the person on: whose records, and whose view of them. */\n hostId: string\n email: string\n origin: PluginRecordOrigin\n /**\n * Only a record this door just started — a person met for the first time.\n * A door that touches people already on file (a capture on a known\n * address) asks for this; one acting on a person deliberately (an\n * enrollment) does not.\n */\n firstTouchOnly?: boolean\n}\n\nexport interface PluginRecordOriginReport {\n /** Records that took the origin. */\n records: number\n}\n\nexport interface PluginRecordOriginWriter {\n /** Never throws: the door has already done what it records. */\n stamp(request: PluginRecordOriginRequest): Promise<PluginRecordOriginReport>\n}\n\nexport const PLUGIN_RECORD_ORIGIN = definePluginServiceContract<PluginRecordOriginWriter>(\n 'core.record-origin',\n { multiple: false },\n)\n\n/**\n * Registers the workspace's record-origin writer. The owner is the loader's\n * marker when a register fn is running, else `options.pluginId`; with\n * neither the registration throws, and a second plugin's writer is refused\n * naming both — the incumbent keeps serving.\n */\nexport function registerPluginRecordOriginWriter(\n writer: PluginRecordOriginWriter,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_RECORD_ORIGIN, writer, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\n/**\n * Records where a person came from through whichever plugin keeps the\n * records, or answers `null` when none does. Never throws: every caller has\n * already met the person, and the origin is bookkeeping beside it.\n */\nexport async function stampRecordOrigin(\n request: PluginRecordOriginRequest,\n): Promise<PluginRecordOriginReport | null> {\n const entry = resolvePluginServices(PLUGIN_RECORD_ORIGIN)[0]\n if (!entry) return null\n try {\n return await entry.impl.stamp(request)\n } catch (error) {\n console.error('[record-origin] the record system could not record where a person came from', error)\n return null\n }\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_ORIGIN","multiple","registerPluginRecordOriginWriter","writer","options","pluginId","stampRecordOrigin","request","entry","impl","stamp","error","console"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AA+D1B,OAAO,MAAMC,uBAAuBH,4BAClC,sBACA;IAAEI,UAAU;AAAM,GACnB;AAED;;;;;CAKC,GACD,OAAO,SAASC,iCACdC,MAAgC,EAChCC,OAA+B;IAE/BN,sBAAsBE,sBAAsBG,QAAQ,aAC9CC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAEA;;;;CAIC,GACD,OAAO,eAAeC,kBACpBC,OAAkC;IAElC,MAAMC,QAAQT,sBAAsBC,qBAAqB,CAAC,EAAE;IAC5D,IAAI,CAACQ,OAAO,OAAO;IACnB,IAAI;QACF,OAAO,MAAMA,MAAMC,IAAI,CAACC,KAAK,CAACH;IAChC,EAAE,OAAOI,OAAO;QACdC,QAAQD,KAAK,CAAC,+EAA+EA;QAC7F,OAAO;IACT;AACF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ export interface PluginRecordPageDeclaration {
18
+ /** The record kind, as `plugin-record-routes` and `plugin-record-index` key it. */
19
+ kind: string;
20
+ /**
21
+ * The site console path the kind is read on, under one of the declaring
22
+ * plugin's own console routes: `/inbox`.
23
+ */
24
+ path: string;
25
+ }
26
+ /** A declaration with the plugin whose page it is. */
27
+ export type ResolvedPluginRecordPage = PluginRecordPageDeclaration & {
28
+ pluginId: string;
29
+ };
30
+ /** Where a kind is read, or `null` when no plugin in this build shows it. */
31
+ export declare function pluginRecordPage(kind: string): ResolvedPluginRecordPage | null;
32
+ /**
33
+ * A notification's link to the page a site's records of `kind` are read on —
34
+ * `/{hostId}{path}`, which the notification layer rewrites onto the site's
35
+ * console address — or `null` when no plugin shows the kind.
36
+ */
37
+ export declare function pluginRecordPageLink(kind: string, hostId: string): string | null;
@@ -0,0 +1,52 @@
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
+ * WHERE A PERSON READS A RECORD KIND, FOR A SERVER THAT TELLS THEM ABOUT ONE
18
+ * (AGL-3080).
19
+ *
20
+ * The console's record routes (`plugin-record-routes`) answer where a record
21
+ * is read for a surface rendered in the console: the plugin that shows the
22
+ * kind registers its addresses from its console registrar, and a link asks for
23
+ * the kind. A SERVER has no console registrar. The forms plugin's door tells a
24
+ * site's managers about a new submission, or that submissions are paused, and
25
+ * the notification's link is where they read submissions — a page the Inbox
26
+ * draws, which the door may not spell.
27
+ *
28
+ * So the plugin whose console page shows a kind DECLARES it in
29
+ * `plugins.config.json` (`recordPages`: the kind, and the site console path
30
+ * under one of its own routes), the manifest generator compiles it, and a
31
+ * server asks here for the kind. Compiled rather than registered for the same
32
+ * reason the host collections are: the tenant process that sends the
33
+ * notification never loads the plugin that draws the page.
34
+ *
35
+ * No declaration is an answer: no plugin shows the kind in this build, and a
36
+ * notification goes without a link rather than to a page that is not there.
37
+ */ import { PLUGIN_RECORD_PAGES_DECLARED } from "./first-party-plugins.generated.js";
38
+ /** Where a kind is read, or `null` when no plugin in this build shows it. */ export function pluginRecordPage(kind) {
39
+ var _PLUGIN_RECORD_PAGES_DECLARED_find;
40
+ const key = kind.trim();
41
+ return (_PLUGIN_RECORD_PAGES_DECLARED_find = PLUGIN_RECORD_PAGES_DECLARED.find((one)=>one.kind === key)) != null ? _PLUGIN_RECORD_PAGES_DECLARED_find : null;
42
+ }
43
+ /**
44
+ * A notification's link to the page a site's records of `kind` are read on —
45
+ * `/{hostId}{path}`, which the notification layer rewrites onto the site's
46
+ * console address — or `null` when no plugin shows the kind.
47
+ */ export function pluginRecordPageLink(kind, hostId) {
48
+ const page = pluginRecordPage(kind);
49
+ return page && hostId ? `/${hostId}${page.path}` : null;
50
+ }
51
+
52
+ //# sourceMappingURL=plugin-record-pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-pages.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * WHERE A PERSON READS A RECORD KIND, FOR A SERVER THAT TELLS THEM ABOUT ONE\n * (AGL-3080).\n *\n * The console's record routes (`plugin-record-routes`) answer where a record\n * is read for a surface rendered in the console: the plugin that shows the\n * kind registers its addresses from its console registrar, and a link asks for\n * the kind. A SERVER has no console registrar. The forms plugin's door tells a\n * site's managers about a new submission, or that submissions are paused, and\n * the notification's link is where they read submissions — a page the Inbox\n * draws, which the door may not spell.\n *\n * So the plugin whose console page shows a kind DECLARES it in\n * `plugins.config.json` (`recordPages`: the kind, and the site console path\n * under one of its own routes), the manifest generator compiles it, and a\n * server asks here for the kind. Compiled rather than registered for the same\n * reason the host collections are: the tenant process that sends the\n * notification never loads the plugin that draws the page.\n *\n * No declaration is an answer: no plugin shows the kind in this build, and a\n * notification goes without a link rather than to a page that is not there.\n */\n\nimport { PLUGIN_RECORD_PAGES_DECLARED } from './first-party-plugins.generated'\n\nexport interface PluginRecordPageDeclaration {\n /** The record kind, as `plugin-record-routes` and `plugin-record-index` key it. */\n kind: string\n /**\n * The site console path the kind is read on, under one of the declaring\n * plugin's own console routes: `/inbox`.\n */\n path: string\n}\n\n/** A declaration with the plugin whose page it is. */\nexport type ResolvedPluginRecordPage = PluginRecordPageDeclaration & { pluginId: string }\n\n/** Where a kind is read, or `null` when no plugin in this build shows it. */\nexport function pluginRecordPage(kind: string): ResolvedPluginRecordPage | null {\n const key = kind.trim()\n return PLUGIN_RECORD_PAGES_DECLARED.find((one) => one.kind === key) ?? null\n}\n\n/**\n * A notification's link to the page a site's records of `kind` are read on —\n * `/{hostId}{path}`, which the notification layer rewrites onto the site's\n * console address — or `null` when no plugin shows the kind.\n */\nexport function pluginRecordPageLink(kind: string, hostId: string): string | null {\n const page = pluginRecordPage(kind)\n return page && hostId ? `/${hostId}${page.path}` : null\n}\n"],"names":["PLUGIN_RECORD_PAGES_DECLARED","pluginRecordPage","kind","key","trim","find","one","pluginRecordPageLink","hostId","page","path"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;CAqBC,GAED,SAASA,4BAA4B,QAAQ,qCAAiC;AAe9E,2EAA2E,GAC3E,OAAO,SAASC,iBAAiBC,IAAY;QAEpCF;IADP,MAAMG,MAAMD,KAAKE,IAAI;IACrB,QAAOJ,qCAAAA,6BAA6BK,IAAI,CAAC,CAACC,MAAQA,IAAIJ,IAAI,KAAKC,gBAAxDH,qCAAgE;AACzE;AAEA;;;;CAIC,GACD,OAAO,SAASO,qBAAqBL,IAAY,EAAEM,MAAc;IAC/D,MAAMC,OAAOR,iBAAiBC;IAC9B,OAAOO,QAAQD,SAAS,CAAC,CAAC,EAAEA,SAASC,KAAKC,IAAI,EAAE,GAAG;AACrD"}