@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
@@ -181,9 +181,61 @@ export interface PluginRecordDeliveryRequest {
181
181
  /** What the server said, scrubbed of the address; the owner bounds it. */
182
182
  detail?: string | null;
183
183
  }
184
+ /**
185
+ * An email a plugin is about to send to a person (AGL-3080), offered to the
186
+ * record system BEFORE it goes so the entry it files can follow the
187
+ * message's delivery: the record system answers with the provider tags the
188
+ * message has to carry for the delivery webhook to find the entry, and with
189
+ * how to file it once the provider has accepted it.
190
+ *
191
+ * Only a message addressed to the person `link` names — the person an
192
+ * automation's event is about — earns an entry: an alert routed to the
193
+ * merchant's own inbox would put that inbox on a customer's history.
194
+ */
195
+ export interface PluginRecordEmailPrepareRequest {
196
+ orgId: string;
197
+ /** The site the message goes out as: the entry carries its scope. */
198
+ hostId: string;
199
+ /** The address the message is sent to, raw. */
200
+ to: string;
201
+ /** The person the sender means: by id where it holds one, else by address. */
202
+ link: Pick<PluginRecordLink, 'contactId' | 'email'>;
203
+ /**
204
+ * The organization's billing document, when the caller already read it —
205
+ * the record system's plan gate and scope read it; absent, the owner reads
206
+ * it itself.
207
+ */
208
+ org?: unknown;
209
+ }
210
+ /** The message as it was sent, for the entry the record system files. */
211
+ export interface PluginRecordEmailSent {
212
+ subject: string;
213
+ body: string;
214
+ to: string;
215
+ /** When it was accepted, epoch ms; now when absent. */
216
+ atMs?: number;
217
+ /** The sender's own id for what sent it — an automation's id. */
218
+ sourceRef: string;
219
+ }
220
+ /** What the record system answered a prepared email with. */
221
+ export interface PluginRecordPreparedEmail {
222
+ /** Provider tags the message must carry for its delivery to reach the entry. */
223
+ tags: ReadonlyArray<{
224
+ name: string;
225
+ value: string;
226
+ }>;
227
+ /** Files the entry, once the provider accepted the message. Never throws. */
228
+ file(sent: PluginRecordEmailSent): Promise<void>;
229
+ }
184
230
  export interface PluginRecordTimelineWriter {
185
231
  logActivity(request: PluginRecordActivityRequest): Promise<PluginRecordWrite>;
186
232
  createTask(request: PluginRecordTaskRequest): Promise<PluginRecordWrite>;
233
+ /**
234
+ * Prepares the entry an email to a person will earn (AGL-3080). Optional:
235
+ * a record system that files no sent mail answers nothing. `null` for a
236
+ * message that earns no entry. Never throws.
237
+ */
238
+ prepareEmail?(request: PluginRecordEmailPrepareRequest): Promise<PluginRecordPreparedEmail | null>;
187
239
  /**
188
240
  * Marks a filed email bounced or reported (AGL-3245). Optional: a record
189
241
  * system that keeps no delivery state answers nothing, and the caller's
@@ -208,3 +260,11 @@ export interface ResolvedPluginRecordTimelineWriter {
208
260
  }
209
261
  /** The writer, with its owner, or `null` when no plugin keeps records here. */
210
262
  export declare function pluginRecordTimelineWriter(): ResolvedPluginRecordTimelineWriter | null;
263
+ /**
264
+ * Asks whichever plugin keeps records to prepare the entry an email to a
265
+ * person will earn, or answers `null` when none does, the one that does
266
+ * files no sent mail, or this message earns no entry. Never throws: the
267
+ * entry is bookkeeping beside a send, and a lookup that failed must not
268
+ * become a message that never left.
269
+ */
270
+ export declare function preparePluginRecordEmail(request: PluginRecordEmailPrepareRequest): Promise<PluginRecordPreparedEmail | null>;
@@ -35,5 +35,21 @@ export const PLUGIN_RECORD_TIMELINE = definePluginServiceContract('core.record-t
35
35
  writer: entry.impl
36
36
  } : null;
37
37
  }
38
+ /**
39
+ * Asks whichever plugin keeps records to prepare the entry an email to a
40
+ * person will earn, or answers `null` when none does, the one that does
41
+ * files no sent mail, or this message earns no entry. Never throws: the
42
+ * entry is bookkeeping beside a send, and a lookup that failed must not
43
+ * become a message that never left.
44
+ */ export async function preparePluginRecordEmail(request) {
45
+ const resolved = pluginRecordTimelineWriter();
46
+ if (!(resolved == null ? void 0 : resolved.writer.prepareEmail)) return null;
47
+ try {
48
+ return await resolved.writer.prepareEmail(request);
49
+ } catch (error) {
50
+ console.error('[record-timeline] the record system could not prepare an email entry', error);
51
+ return null;
52
+ }
53
+ }
38
54
 
39
55
  //# sourceMappingURL=plugin-record-timeline.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-timeline.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 * An entry one plugin files on a record another plugin keeps (AGL-2981).\n *\n * A person and the company they work for are records of the workspace's\n * record system, and what happened with them — an email sent, a reply\n * received, a call to make — belongs on their timeline, whichever plugin it\n * happened in.\n * A plugin that sends mail or books a meeting must not write the record\n * system's documents itself: it would restate the owner's rules — where an\n * entry lives, who may see it, how a copy of the same email is told apart,\n * how many entries a record may carry — and drift from them the day the\n * owner changes one. Nor may it import the owner, which the package map\n * forbids.\n *\n * So the record system registers a WRITER here, from its server surfaces,\n * and a caller asks for it. Every rule stays the owner's:\n *\n * - the entry is stamped with the scope a record made on the site would\n * carry, so it is visible to exactly who sees the record;\n * - an EMAIL is filed once per `Message-ID`, under the id the owner files a\n * copy of the same message by through any other door — a copy the rep\n * forwarded to a capture address is the same entry, not a second one;\n * - any other entry, and every task, is filed once per the caller's own\n * `dedupeKey`, so a caller that runs again finds what it wrote rather\n * than writing it twice;\n * - a record whose log is full, or a workspace whose plan carries no record\n * system, is refused — answered, never thrown;\n * - a caller that knows a record only as the owner handed it over — a kind\n * and an id carried on a link — or knows only the person's address is\n * answered with the record the owner finds for it, on that site, or\n * refused with a 404 when it finds none.\n *\n * A single-implementation contract: a workspace keeps one record system.\n *\n * ## The caller proves who is asking\n *\n * Like `plugin-resource-drafts`, the registry authenticates nobody. The\n * caller decides, in its own terms and before it asks, that the entry is\n * the workspace's to write — that the record is one it acted on, for the\n * member it acted for.\n */\n\n/**\n * The record an entry is filed on: the person (a contact, or a lead not yet\n * one) and the company they work for, as the owner links them — or, from a\n * caller that holds no id of the owner's, how to find the record.\n *\n * The ids win where given, and are filed on as named. Otherwise `record`,\n * then `email`: the owner looks the record up as the entry's site sees it,\n * links whatever it groups that record under itself, and refuses the entry\n * with a 404 when it finds nothing.\n */\nexport interface PluginRecordLink {\n contactId?: string\n companyId?: string\n /**\n * A lead not yet converted (AGL-3234) — the record system's own id for\n * it, addressed under `hostId`. A sequence's emails and tasks land on the\n * lead's page while the person is one, and follow the lead to the contact\n * it becomes.\n */\n leadId?: string\n /**\n * A record the owner handed the caller, by its kind and id in the owner's\n * own words, and carried back unread (AGL-2660): the record a booking link\n * was dropped from, through the zone the owner drew it in. A kind the\n * owner does not keep, or a record the site cannot see, is passed over for\n * `email` rather than refused.\n */\n record?: { kind: string; id: string } | null\n /**\n * The person at this address, as the entry's site knows them: what the\n * entry lands on when nothing above names a record the owner finds — a\n * booking taken off the widget cold.\n */\n email?: string | null\n}\n\n/** Where an entry is filed, and by which plugin. */\nexport interface PluginRecordEntryContext {\n orgId: string\n /** The site whose scope the entry carries: as if a member made it there. */\n hostId: string\n link: PluginRecordLink\n /** The plugin filing it, recorded on the entry. */\n sourcePluginId: string\n}\n\n/** What an activity is, in the record system's own words. */\nexport type PluginRecordActivityKind = 'email' | 'call' | 'meeting' | 'note' | 'other'\n\n/** The message an `email` activity is about. */\nexport interface PluginRecordEmail {\n /** `outbound` for one the workspace wrote; `inbound` for one written to it. */\n direction: 'outbound' | 'inbound'\n subject: string\n /** The address on the message's From. */\n from: string\n /** The address it was written to, when one is worth showing. */\n to: string | null\n /** The `Message-ID` header, angle brackets included: what files it once. */\n messageId: string\n /** The `Message-ID` it answered, when it answered one. */\n inReplyTo?: string | null\n}\n\nexport interface PluginRecordActivityRequest extends PluginRecordEntryContext {\n kind: PluginRecordActivityKind\n /** When it happened, epoch ms. */\n atMs: number\n /** Plain text: an email's excerpt, a note's words. The owner bounds it. */\n body: string\n /** The member it is by, or `''` for none. */\n byUid: string\n byName?: string | null\n /** Required for an `email`, and read only for one. */\n email?: PluginRecordEmail | null\n /** Required for every kind but `email`: the caller's id for this entry. */\n dedupeKey?: string | null\n}\n\n/** What a task is, in the record system's own words. */\nexport type PluginRecordTaskKind = 'call' | 'email' | 'meeting' | 'todo'\n\nexport interface PluginRecordTaskRequest extends PluginRecordEntryContext {\n /** The caller's id for this task: the same key never makes a second task. */\n dedupeKey: string\n title: string\n notes?: string | null\n kind: PluginRecordTaskKind\n /** When it is due, epoch ms. */\n dueAtMs: number\n /**\n * The member it is for, `''` for nobody, or `null` for whoever holds the\n * record it lands on — the caller that found the record by `record` or\n * `email` does not know who that is.\n */\n assigneeUid: string | null\n /** The member who made it happen, or `''` when nobody did. */\n createdByUid: string\n}\n\n/**\n * The owner's answer. `created: false` is the entry a previous write — or a\n * copy through another door — already filed, under the same `id`.\n */\nexport type PluginRecordWrite =\n | { ok: true; id: string; created: boolean }\n | {\n ok: false\n status: 400 | 403 | 404 | 409\n /** Customer-safe: a caller may show or log it as it stands. */\n error: string\n }\n\n/**\n * What became of an email a plugin filed (AGL-3245): the receiving server\n * bounced it, or the recipient reported it. Addressed by the `Message-ID`\n * the send was filed under, so the verdict lands on the send's own entry\n * and the record reads Sent, then Bounced.\n */\nexport interface PluginRecordDeliveryRequest {\n orgId: string\n /** The `Message-ID` header of the send, angle brackets included. */\n messageId: string\n state: 'bounced' | 'complained'\n /** When it happened, epoch ms. */\n atMs: number\n /** What the server said, scrubbed of the address; the owner bounds it. */\n detail?: string | null\n}\n\nexport interface PluginRecordTimelineWriter {\n logActivity(request: PluginRecordActivityRequest): Promise<PluginRecordWrite>\n createTask(request: PluginRecordTaskRequest): Promise<PluginRecordWrite>\n /**\n * Marks a filed email bounced or reported (AGL-3245). Optional: a record\n * system that keeps no delivery state answers nothing, and the caller's\n * entry stands as filed.\n */\n recordEmailDelivery?(request: PluginRecordDeliveryRequest): Promise<PluginRecordWrite>\n}\n\nexport const PLUGIN_RECORD_TIMELINE = definePluginServiceContract<PluginRecordTimelineWriter>(\n 'core.record-timeline',\n { multiple: false },\n)\n\n/**\n * Registers the workspace's record timeline writer. The owner is the loader's\n * marker when a register fn is running, else `options.pluginId`; with neither\n * the registration throws, and a second plugin's writer is refused naming\n * both — the incumbent keeps serving.\n */\nexport function registerPluginRecordTimelineWriter(\n writer: PluginRecordTimelineWriter,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_RECORD_TIMELINE, writer, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\nexport interface ResolvedPluginRecordTimelineWriter {\n /** The plugin that keeps the records. */\n pluginId: string\n writer: PluginRecordTimelineWriter\n}\n\n/** The writer, with its owner, or `null` when no plugin keeps records here. */\nexport function pluginRecordTimelineWriter(): ResolvedPluginRecordTimelineWriter | null {\n const entry = resolvePluginServices(PLUGIN_RECORD_TIMELINE)[0]\n return entry ? { pluginId: entry.pluginId, writer: entry.impl } : null\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_TIMELINE","multiple","registerPluginRecordTimelineWriter","writer","options","pluginId","pluginRecordTimelineWriter","entry","impl"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAyL1B,OAAO,MAAMC,yBAAyBH,4BACpC,wBACA;IAAEI,UAAU;AAAM,GACnB;AAED;;;;;CAKC,GACD,OAAO,SAASC,mCACdC,MAAkC,EAClCC,OAA+B;IAE/BN,sBAAsBE,wBAAwBG,QAAQ,aAChDC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAQA,6EAA6E,GAC7E,OAAO,SAASC;IACd,MAAMC,QAAQR,sBAAsBC,uBAAuB,CAAC,EAAE;IAC9D,OAAOO,QAAQ;QAAEF,UAAUE,MAAMF,QAAQ;QAAEF,QAAQI,MAAMC,IAAI;IAAC,IAAI;AACpE"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-record-timeline.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 * An entry one plugin files on a record another plugin keeps (AGL-2981).\n *\n * A person and the company they work for are records of the workspace's\n * record system, and what happened with them — an email sent, a reply\n * received, a call to make — belongs on their timeline, whichever plugin it\n * happened in.\n * A plugin that sends mail or books a meeting must not write the record\n * system's documents itself: it would restate the owner's rules — where an\n * entry lives, who may see it, how a copy of the same email is told apart,\n * how many entries a record may carry — and drift from them the day the\n * owner changes one. Nor may it import the owner, which the package map\n * forbids.\n *\n * So the record system registers a WRITER here, from its server surfaces,\n * and a caller asks for it. Every rule stays the owner's:\n *\n * - the entry is stamped with the scope a record made on the site would\n * carry, so it is visible to exactly who sees the record;\n * - an EMAIL is filed once per `Message-ID`, under the id the owner files a\n * copy of the same message by through any other door — a copy the rep\n * forwarded to a capture address is the same entry, not a second one;\n * - any other entry, and every task, is filed once per the caller's own\n * `dedupeKey`, so a caller that runs again finds what it wrote rather\n * than writing it twice;\n * - a record whose log is full, or a workspace whose plan carries no record\n * system, is refused — answered, never thrown;\n * - a caller that knows a record only as the owner handed it over — a kind\n * and an id carried on a link — or knows only the person's address is\n * answered with the record the owner finds for it, on that site, or\n * refused with a 404 when it finds none.\n *\n * A single-implementation contract: a workspace keeps one record system.\n *\n * ## The caller proves who is asking\n *\n * Like `plugin-resource-drafts`, the registry authenticates nobody. The\n * caller decides, in its own terms and before it asks, that the entry is\n * the workspace's to write — that the record is one it acted on, for the\n * member it acted for.\n */\n\n/**\n * The record an entry is filed on: the person (a contact, or a lead not yet\n * one) and the company they work for, as the owner links them — or, from a\n * caller that holds no id of the owner's, how to find the record.\n *\n * The ids win where given, and are filed on as named. Otherwise `record`,\n * then `email`: the owner looks the record up as the entry's site sees it,\n * links whatever it groups that record under itself, and refuses the entry\n * with a 404 when it finds nothing.\n */\nexport interface PluginRecordLink {\n contactId?: string\n companyId?: string\n /**\n * A lead not yet converted (AGL-3234) — the record system's own id for\n * it, addressed under `hostId`. A sequence's emails and tasks land on the\n * lead's page while the person is one, and follow the lead to the contact\n * it becomes.\n */\n leadId?: string\n /**\n * A record the owner handed the caller, by its kind and id in the owner's\n * own words, and carried back unread (AGL-2660): the record a booking link\n * was dropped from, through the zone the owner drew it in. A kind the\n * owner does not keep, or a record the site cannot see, is passed over for\n * `email` rather than refused.\n */\n record?: { kind: string; id: string } | null\n /**\n * The person at this address, as the entry's site knows them: what the\n * entry lands on when nothing above names a record the owner finds — a\n * booking taken off the widget cold.\n */\n email?: string | null\n}\n\n/** Where an entry is filed, and by which plugin. */\nexport interface PluginRecordEntryContext {\n orgId: string\n /** The site whose scope the entry carries: as if a member made it there. */\n hostId: string\n link: PluginRecordLink\n /** The plugin filing it, recorded on the entry. */\n sourcePluginId: string\n}\n\n/** What an activity is, in the record system's own words. */\nexport type PluginRecordActivityKind = 'email' | 'call' | 'meeting' | 'note' | 'other'\n\n/** The message an `email` activity is about. */\nexport interface PluginRecordEmail {\n /** `outbound` for one the workspace wrote; `inbound` for one written to it. */\n direction: 'outbound' | 'inbound'\n subject: string\n /** The address on the message's From. */\n from: string\n /** The address it was written to, when one is worth showing. */\n to: string | null\n /** The `Message-ID` header, angle brackets included: what files it once. */\n messageId: string\n /** The `Message-ID` it answered, when it answered one. */\n inReplyTo?: string | null\n}\n\nexport interface PluginRecordActivityRequest extends PluginRecordEntryContext {\n kind: PluginRecordActivityKind\n /** When it happened, epoch ms. */\n atMs: number\n /** Plain text: an email's excerpt, a note's words. The owner bounds it. */\n body: string\n /** The member it is by, or `''` for none. */\n byUid: string\n byName?: string | null\n /** Required for an `email`, and read only for one. */\n email?: PluginRecordEmail | null\n /** Required for every kind but `email`: the caller's id for this entry. */\n dedupeKey?: string | null\n}\n\n/** What a task is, in the record system's own words. */\nexport type PluginRecordTaskKind = 'call' | 'email' | 'meeting' | 'todo'\n\nexport interface PluginRecordTaskRequest extends PluginRecordEntryContext {\n /** The caller's id for this task: the same key never makes a second task. */\n dedupeKey: string\n title: string\n notes?: string | null\n kind: PluginRecordTaskKind\n /** When it is due, epoch ms. */\n dueAtMs: number\n /**\n * The member it is for, `''` for nobody, or `null` for whoever holds the\n * record it lands on — the caller that found the record by `record` or\n * `email` does not know who that is.\n */\n assigneeUid: string | null\n /** The member who made it happen, or `''` when nobody did. */\n createdByUid: string\n}\n\n/**\n * The owner's answer. `created: false` is the entry a previous write — or a\n * copy through another door — already filed, under the same `id`.\n */\nexport type PluginRecordWrite =\n | { ok: true; id: string; created: boolean }\n | {\n ok: false\n status: 400 | 403 | 404 | 409\n /** Customer-safe: a caller may show or log it as it stands. */\n error: string\n }\n\n/**\n * What became of an email a plugin filed (AGL-3245): the receiving server\n * bounced it, or the recipient reported it. Addressed by the `Message-ID`\n * the send was filed under, so the verdict lands on the send's own entry\n * and the record reads Sent, then Bounced.\n */\nexport interface PluginRecordDeliveryRequest {\n orgId: string\n /** The `Message-ID` header of the send, angle brackets included. */\n messageId: string\n state: 'bounced' | 'complained'\n /** When it happened, epoch ms. */\n atMs: number\n /** What the server said, scrubbed of the address; the owner bounds it. */\n detail?: string | null\n}\n\n/**\n * An email a plugin is about to send to a person (AGL-3080), offered to the\n * record system BEFORE it goes so the entry it files can follow the\n * message's delivery: the record system answers with the provider tags the\n * message has to carry for the delivery webhook to find the entry, and with\n * how to file it once the provider has accepted it.\n *\n * Only a message addressed to the person `link` names — the person an\n * automation's event is about — earns an entry: an alert routed to the\n * merchant's own inbox would put that inbox on a customer's history.\n */\nexport interface PluginRecordEmailPrepareRequest {\n orgId: string\n /** The site the message goes out as: the entry carries its scope. */\n hostId: string\n /** The address the message is sent to, raw. */\n to: string\n /** The person the sender means: by id where it holds one, else by address. */\n link: Pick<PluginRecordLink, 'contactId' | 'email'>\n /**\n * The organization's billing document, when the caller already read it —\n * the record system's plan gate and scope read it; absent, the owner reads\n * it itself.\n */\n org?: unknown\n}\n\n/** The message as it was sent, for the entry the record system files. */\nexport interface PluginRecordEmailSent {\n subject: string\n body: string\n to: string\n /** When it was accepted, epoch ms; now when absent. */\n atMs?: number\n /** The sender's own id for what sent it — an automation's id. */\n sourceRef: string\n}\n\n/** What the record system answered a prepared email with. */\nexport interface PluginRecordPreparedEmail {\n /** Provider tags the message must carry for its delivery to reach the entry. */\n tags: ReadonlyArray<{ name: string; value: string }>\n /** Files the entry, once the provider accepted the message. Never throws. */\n file(sent: PluginRecordEmailSent): Promise<void>\n}\n\nexport interface PluginRecordTimelineWriter {\n logActivity(request: PluginRecordActivityRequest): Promise<PluginRecordWrite>\n createTask(request: PluginRecordTaskRequest): Promise<PluginRecordWrite>\n /**\n * Prepares the entry an email to a person will earn (AGL-3080). Optional:\n * a record system that files no sent mail answers nothing. `null` for a\n * message that earns no entry. Never throws.\n */\n prepareEmail?(request: PluginRecordEmailPrepareRequest): Promise<PluginRecordPreparedEmail | null>\n /**\n * Marks a filed email bounced or reported (AGL-3245). Optional: a record\n * system that keeps no delivery state answers nothing, and the caller's\n * entry stands as filed.\n */\n recordEmailDelivery?(request: PluginRecordDeliveryRequest): Promise<PluginRecordWrite>\n}\n\nexport const PLUGIN_RECORD_TIMELINE = definePluginServiceContract<PluginRecordTimelineWriter>(\n 'core.record-timeline',\n { multiple: false },\n)\n\n/**\n * Registers the workspace's record timeline writer. The owner is the loader's\n * marker when a register fn is running, else `options.pluginId`; with neither\n * the registration throws, and a second plugin's writer is refused naming\n * both — the incumbent keeps serving.\n */\nexport function registerPluginRecordTimelineWriter(\n writer: PluginRecordTimelineWriter,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_RECORD_TIMELINE, writer, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\nexport interface ResolvedPluginRecordTimelineWriter {\n /** The plugin that keeps the records. */\n pluginId: string\n writer: PluginRecordTimelineWriter\n}\n\n/** The writer, with its owner, or `null` when no plugin keeps records here. */\nexport function pluginRecordTimelineWriter(): ResolvedPluginRecordTimelineWriter | null {\n const entry = resolvePluginServices(PLUGIN_RECORD_TIMELINE)[0]\n return entry ? { pluginId: entry.pluginId, writer: entry.impl } : null\n}\n\n/**\n * Asks whichever plugin keeps records to prepare the entry an email to a\n * person will earn, or answers `null` when none does, the one that does\n * files no sent mail, or this message earns no entry. Never throws: the\n * entry is bookkeeping beside a send, and a lookup that failed must not\n * become a message that never left.\n */\nexport async function preparePluginRecordEmail(\n request: PluginRecordEmailPrepareRequest,\n): Promise<PluginRecordPreparedEmail | null> {\n const resolved = pluginRecordTimelineWriter()\n if (!resolved?.writer.prepareEmail) return null\n try {\n return await resolved.writer.prepareEmail(request)\n } catch (error) {\n console.error('[record-timeline] the record system could not prepare an email entry', error)\n return null\n }\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RECORD_TIMELINE","multiple","registerPluginRecordTimelineWriter","writer","options","pluginId","pluginRecordTimelineWriter","entry","impl","preparePluginRecordEmail","request","resolved","prepareEmail","error","console"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AA6O1B,OAAO,MAAMC,yBAAyBH,4BACpC,wBACA;IAAEI,UAAU;AAAM,GACnB;AAED;;;;;CAKC,GACD,OAAO,SAASC,mCACdC,MAAkC,EAClCC,OAA+B;IAE/BN,sBAAsBE,wBAAwBG,QAAQ,aAChDC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAQA,6EAA6E,GAC7E,OAAO,SAASC;IACd,MAAMC,QAAQR,sBAAsBC,uBAAuB,CAAC,EAAE;IAC9D,OAAOO,QAAQ;QAAEF,UAAUE,MAAMF,QAAQ;QAAEF,QAAQI,MAAMC,IAAI;IAAC,IAAI;AACpE;AAEA;;;;;;CAMC,GACD,OAAO,eAAeC,yBACpBC,OAAwC;IAExC,MAAMC,WAAWL;IACjB,IAAI,EAACK,4BAAAA,SAAUR,MAAM,CAACS,YAAY,GAAE,OAAO;IAC3C,IAAI;QACF,OAAO,MAAMD,SAASR,MAAM,CAACS,YAAY,CAACF;IAC5C,EAAE,OAAOG,OAAO;QACdC,QAAQD,KAAK,CAAC,wEAAwEA;QACtF,OAAO;IACT;AACF"}
@@ -45,11 +45,11 @@ import type { ReusableComponentTree } from '../app-utils/compose-reusable-compon
45
45
  *
46
46
  * ## The registry carries no authorization of the CALLER
47
47
  *
48
- * Like `order-fulfilment`, a writer is a pre-authorized operation. It applies
49
- * the owner's rules to the member the draft is for, and knows nothing about
50
- * whether the caller may act for that member. A caller establishes, in its
51
- * own terms and before it asks: who is asking, that the org owns the site,
52
- * and that the owning plugin is switched on for that site.
48
+ * A writer is a pre-authorized operation. It applies the owner's rules to the
49
+ * member the draft is for, and knows nothing about whether the caller may act
50
+ * for that member. A caller establishes, in its own terms and before it asks:
51
+ * who is asking, that the org owns the site, and that the owning plugin is
52
+ * switched on for that site.
53
53
  *
54
54
  * ## One writer per resource
55
55
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-resource-drafts.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 { ReusableComponentTree } from '../app-utils/compose-reusable-components'\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * Drafts one plugin writes for another (AGL-2912).\n *\n * A resource belongs to the plugin that models it: the document's shape, the\n * allowance a create counts against, who may create one, and what makes one\n * well-formed. A different plugin that produces such a resource — an importer\n * turning a file into pages, a generator turning a brief into an email, a\n * migration bringing campaigns over from another tool — must not write the\n * document itself, because it would restate the owner's rules and drift from\n * them the day the owner changes one. Nor may it import the owner, which the\n * package map forbids.\n *\n * So the owner registers a WRITER for the resource from its server surfaces,\n * and a caller asks for one by resource name. Every rule stays the owner's:\n *\n * - `refusal` answers, before the caller spends anything, whether this\n * member may create one on this site now: their role, the site's room;\n * - `check` answers, synchronously and without I/O, whether content is\n * well-formed, so a caller can hold what it produces to the owner's rules\n * before it keeps it;\n * - `read` reports the draft already written under an id, so a caller that\n * runs again finds its draft rather than making a second;\n * - `write` makes the draft, meets the allowance inside its own write, and\n * never publishes, schedules or sends anything.\n *\n * Built on the typed service registry (`plugin-services`): one contract, every\n * writer registered under its resource name as the service's key.\n *\n * ## The registry carries no authorization of the CALLER\n *\n * Like `order-fulfilment`, a writer is a pre-authorized operation. It applies\n * the owner's rules to the member the draft is for, and knows nothing about\n * whether the caller may act for that member. A caller establishes, in its\n * own terms and before it asks: who is asking, that the org owns the site,\n * and that the owning plugin is switched on for that site.\n *\n * ## One writer per resource\n *\n * A resource has one owner. A second plugin registering a writer for a\n * resource another plugin already writes is refused, naming both, and the\n * incumbent keeps serving; the same plugin registering again — from its second\n * server surface, or on a hot reload — replaces its own.\n */\n\n/** Who a draft is for, on which site, and when. */\nexport interface PluginDraftContext {\n orgId: string\n hostId: string\n /** The member the draft is made for: the owner applies its rules to them and records them as the author. */\n uid: string\n /** The org document the caller already read, for the owner's entitlement and allowance reads. */\n org: Readonly<Record<string, unknown>> | null\n now: Date\n}\n\n/** Why a draft cannot be made. */\nexport interface PluginDraftRefusal {\n status: 400 | 403 | 404 | 409\n /** Customer-safe: a caller shows it as it stands. */\n error: string\n}\n\n/** A draft that exists. */\nexport interface PluginDraftRecord {\n id: string\n name: string\n /** The version the draft opens on, where the resource has versions. */\n versionId: string | null\n /** What the owner reports about the draft beyond its identity, in the shape its writer documents. */\n facts: Readonly<Record<string, unknown>>\n}\n\nexport interface PluginDraftRequest extends PluginDraftContext {\n /** The draft's id, chosen by the caller: one per act, so a write asked again finds its draft. */\n id: string\n /** The name asked for; the owner may number it to keep it unique. */\n name: string\n /** The resource's content, in the shape the owner's writer documents and validates. */\n content: Readonly<Record<string, unknown>>\n}\n\nexport type PluginDraftCheck =\n | { ok: true; facts: Readonly<Record<string, unknown>> }\n | { ok: false; problems: string[] }\n\n/** What a check is told beside the content. */\nexport interface PluginDraftCheckContext {\n hostId: string\n /**\n * The site's published reusable components the content places, keyed by id\n * — what `loadReferencedComponents` reads (AGL-3287).\n *\n * `check` does no I/O, so a caller whose content may place a component loads\n * the definitions first and hands them in; a writer whose resource renders\n * placed components composes them before judging, so what passes is what\n * would render. Absent means none were loaded, and the content is judged as\n * written.\n */\n components?: Readonly<Record<string, ReusableComponentTree | undefined>>\n}\n\nexport type PluginDraftWrite =\n | (PluginDraftRecord & { ok: true; replayed: boolean })\n | (PluginDraftRefusal & { ok: false })\n\nexport interface PluginResourceDraftWriter {\n /** Whether this member may create one on this site now; `null` admits. */\n refusal(context: PluginDraftContext): Promise<PluginDraftRefusal | null>\n /** Whether content is well-formed, with what the owner derived from it. Pure. */\n check(\n content: Readonly<Record<string, unknown>>,\n context: PluginDraftCheckContext,\n ): PluginDraftCheck\n /** The draft written under `id`, or `null`. */\n read(context: { hostId: string; id: string }): Promise<PluginDraftRecord | null>\n /** Makes the draft; a refusal is returned, never thrown. */\n write(request: PluginDraftRequest): Promise<PluginDraftWrite>\n}\n\nexport const PLUGIN_RESOURCE_DRAFTS =\n definePluginServiceContract<PluginResourceDraftWriter>('core.resource-drafts', {\n multiple: true,\n })\n\n/**\n * Registers the writer for a resource. The owner is the loader's marker when\n * a register fn is running, else `options.pluginId`; with neither the\n * registration throws. A writer for a resource another plugin writes throws\n * naming both.\n */\nexport function registerPluginResourceDraftWriter(\n resource: string,\n writer: PluginResourceDraftWriter,\n options?: { pluginId?: string },\n): void {\n const key = resource.trim()\n if (!key) throw new Error('a resource draft writer needs a resource name')\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n const incumbent = resolvePluginServices(PLUGIN_RESOURCE_DRAFTS).find(\n (entry) => entry.key === key,\n )\n if (incumbent && pluginId && incumbent.pluginId !== pluginId) {\n throw new Error(\n `resource \"${key}\" already has a draft writer from \"${incumbent.pluginId}\"; ` +\n `refused \"${pluginId}\"`,\n )\n }\n registerPluginService(PLUGIN_RESOURCE_DRAFTS, writer, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n key,\n })\n}\n\nexport interface ResolvedPluginDraftWriter {\n /** The plugin that owns the resource. */\n pluginId: string\n writer: PluginResourceDraftWriter\n}\n\n/** The writer for a resource, with its owner, or `null` when no plugin writes it. */\nexport function pluginResourceDraftWriter(resource: string): ResolvedPluginDraftWriter | null {\n const key = resource.trim()\n const entry = resolvePluginServices(PLUGIN_RESOURCE_DRAFTS).find((one) => one.key === key)\n return entry ? { pluginId: entry.pluginId, writer: entry.impl } : null\n}\n"],"names":["getRegisteringPluginId","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RESOURCE_DRAFTS","multiple","registerPluginResourceDraftWriter","resource","writer","options","key","trim","Error","pluginId","incumbent","find","entry","pluginResourceDraftWriter","one","impl"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,sBAAsB,QAAQ,qCAAiC;AACxE,SACEC,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAyH1B,OAAO,MAAMC,yBACXH,4BAAuD,wBAAwB;IAC7EI,UAAU;AACZ,GAAE;AAEJ;;;;;CAKC,GACD,OAAO,SAASC,kCACdC,QAAgB,EAChBC,MAAiC,EACjCC,OAA+B;QAIbT,MAAAA;IAFlB,MAAMU,MAAMH,SAASI,IAAI;IACzB,IAAI,CAACD,KAAK,MAAM,IAAIE,MAAM;IAC1B,MAAMC,WAAW,EAACb,QAAAA,0BAAAA,oCAAAA,0BAA4BS,2BAAAA,QAASI,QAAQ,YAA7Cb,OAAiD,IAAIW,IAAI;IAC3E,MAAMG,YAAYX,sBAAsBC,wBAAwBW,IAAI,CAClE,CAACC,QAAUA,MAAMN,GAAG,KAAKA;IAE3B,IAAII,aAAaD,YAAYC,UAAUD,QAAQ,KAAKA,UAAU;QAC5D,MAAM,IAAID,MACR,CAAC,UAAU,EAAEF,IAAI,mCAAmC,EAAEI,UAAUD,QAAQ,CAAC,GAAG,CAAC,GAC3E,CAAC,SAAS,EAAEA,SAAS,CAAC,CAAC;IAE7B;IACAX,sBAAsBE,wBAAwBI,QAAQ,aAChDC,CAAAA,2BAAAA,QAASI,QAAQ,IAAG;QAAEA,UAAUJ,QAAQI,QAAQ;IAAC,IAAI,CAAC;QAC1DH;;AAEJ;AAQA,mFAAmF,GACnF,OAAO,SAASO,0BAA0BV,QAAgB;IACxD,MAAMG,MAAMH,SAASI,IAAI;IACzB,MAAMK,QAAQb,sBAAsBC,wBAAwBW,IAAI,CAAC,CAACG,MAAQA,IAAIR,GAAG,KAAKA;IACtF,OAAOM,QAAQ;QAAEH,UAAUG,MAAMH,QAAQ;QAAEL,QAAQQ,MAAMG,IAAI;IAAC,IAAI;AACpE"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-resource-drafts.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 { ReusableComponentTree } from '../app-utils/compose-reusable-components'\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * Drafts one plugin writes for another (AGL-2912).\n *\n * A resource belongs to the plugin that models it: the document's shape, the\n * allowance a create counts against, who may create one, and what makes one\n * well-formed. A different plugin that produces such a resource — an importer\n * turning a file into pages, a generator turning a brief into an email, a\n * migration bringing campaigns over from another tool — must not write the\n * document itself, because it would restate the owner's rules and drift from\n * them the day the owner changes one. Nor may it import the owner, which the\n * package map forbids.\n *\n * So the owner registers a WRITER for the resource from its server surfaces,\n * and a caller asks for one by resource name. Every rule stays the owner's:\n *\n * - `refusal` answers, before the caller spends anything, whether this\n * member may create one on this site now: their role, the site's room;\n * - `check` answers, synchronously and without I/O, whether content is\n * well-formed, so a caller can hold what it produces to the owner's rules\n * before it keeps it;\n * - `read` reports the draft already written under an id, so a caller that\n * runs again finds its draft rather than making a second;\n * - `write` makes the draft, meets the allowance inside its own write, and\n * never publishes, schedules or sends anything.\n *\n * Built on the typed service registry (`plugin-services`): one contract, every\n * writer registered under its resource name as the service's key.\n *\n * ## The registry carries no authorization of the CALLER\n *\n * A writer is a pre-authorized operation. It applies the owner's rules to the\n * member the draft is for, and knows nothing about whether the caller may act\n * for that member. A caller establishes, in its own terms and before it asks:\n * who is asking, that the org owns the site, and that the owning plugin is\n * switched on for that site.\n *\n * ## One writer per resource\n *\n * A resource has one owner. A second plugin registering a writer for a\n * resource another plugin already writes is refused, naming both, and the\n * incumbent keeps serving; the same plugin registering again — from its second\n * server surface, or on a hot reload — replaces its own.\n */\n\n/** Who a draft is for, on which site, and when. */\nexport interface PluginDraftContext {\n orgId: string\n hostId: string\n /** The member the draft is made for: the owner applies its rules to them and records them as the author. */\n uid: string\n /** The org document the caller already read, for the owner's entitlement and allowance reads. */\n org: Readonly<Record<string, unknown>> | null\n now: Date\n}\n\n/** Why a draft cannot be made. */\nexport interface PluginDraftRefusal {\n status: 400 | 403 | 404 | 409\n /** Customer-safe: a caller shows it as it stands. */\n error: string\n}\n\n/** A draft that exists. */\nexport interface PluginDraftRecord {\n id: string\n name: string\n /** The version the draft opens on, where the resource has versions. */\n versionId: string | null\n /** What the owner reports about the draft beyond its identity, in the shape its writer documents. */\n facts: Readonly<Record<string, unknown>>\n}\n\nexport interface PluginDraftRequest extends PluginDraftContext {\n /** The draft's id, chosen by the caller: one per act, so a write asked again finds its draft. */\n id: string\n /** The name asked for; the owner may number it to keep it unique. */\n name: string\n /** The resource's content, in the shape the owner's writer documents and validates. */\n content: Readonly<Record<string, unknown>>\n}\n\nexport type PluginDraftCheck =\n | { ok: true; facts: Readonly<Record<string, unknown>> }\n | { ok: false; problems: string[] }\n\n/** What a check is told beside the content. */\nexport interface PluginDraftCheckContext {\n hostId: string\n /**\n * The site's published reusable components the content places, keyed by id\n * — what `loadReferencedComponents` reads (AGL-3287).\n *\n * `check` does no I/O, so a caller whose content may place a component loads\n * the definitions first and hands them in; a writer whose resource renders\n * placed components composes them before judging, so what passes is what\n * would render. Absent means none were loaded, and the content is judged as\n * written.\n */\n components?: Readonly<Record<string, ReusableComponentTree | undefined>>\n}\n\nexport type PluginDraftWrite =\n | (PluginDraftRecord & { ok: true; replayed: boolean })\n | (PluginDraftRefusal & { ok: false })\n\nexport interface PluginResourceDraftWriter {\n /** Whether this member may create one on this site now; `null` admits. */\n refusal(context: PluginDraftContext): Promise<PluginDraftRefusal | null>\n /** Whether content is well-formed, with what the owner derived from it. Pure. */\n check(\n content: Readonly<Record<string, unknown>>,\n context: PluginDraftCheckContext,\n ): PluginDraftCheck\n /** The draft written under `id`, or `null`. */\n read(context: { hostId: string; id: string }): Promise<PluginDraftRecord | null>\n /** Makes the draft; a refusal is returned, never thrown. */\n write(request: PluginDraftRequest): Promise<PluginDraftWrite>\n}\n\nexport const PLUGIN_RESOURCE_DRAFTS =\n definePluginServiceContract<PluginResourceDraftWriter>('core.resource-drafts', {\n multiple: true,\n })\n\n/**\n * Registers the writer for a resource. The owner is the loader's marker when\n * a register fn is running, else `options.pluginId`; with neither the\n * registration throws. A writer for a resource another plugin writes throws\n * naming both.\n */\nexport function registerPluginResourceDraftWriter(\n resource: string,\n writer: PluginResourceDraftWriter,\n options?: { pluginId?: string },\n): void {\n const key = resource.trim()\n if (!key) throw new Error('a resource draft writer needs a resource name')\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n const incumbent = resolvePluginServices(PLUGIN_RESOURCE_DRAFTS).find(\n (entry) => entry.key === key,\n )\n if (incumbent && pluginId && incumbent.pluginId !== pluginId) {\n throw new Error(\n `resource \"${key}\" already has a draft writer from \"${incumbent.pluginId}\"; ` +\n `refused \"${pluginId}\"`,\n )\n }\n registerPluginService(PLUGIN_RESOURCE_DRAFTS, writer, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n key,\n })\n}\n\nexport interface ResolvedPluginDraftWriter {\n /** The plugin that owns the resource. */\n pluginId: string\n writer: PluginResourceDraftWriter\n}\n\n/** The writer for a resource, with its owner, or `null` when no plugin writes it. */\nexport function pluginResourceDraftWriter(resource: string): ResolvedPluginDraftWriter | null {\n const key = resource.trim()\n const entry = resolvePluginServices(PLUGIN_RESOURCE_DRAFTS).find((one) => one.key === key)\n return entry ? { pluginId: entry.pluginId, writer: entry.impl } : null\n}\n"],"names":["getRegisteringPluginId","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_RESOURCE_DRAFTS","multiple","registerPluginResourceDraftWriter","resource","writer","options","key","trim","Error","pluginId","incumbent","find","entry","pluginResourceDraftWriter","one","impl"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,sBAAsB,QAAQ,qCAAiC;AACxE,SACEC,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AAyH1B,OAAO,MAAMC,yBACXH,4BAAuD,wBAAwB;IAC7EI,UAAU;AACZ,GAAE;AAEJ;;;;;CAKC,GACD,OAAO,SAASC,kCACdC,QAAgB,EAChBC,MAAiC,EACjCC,OAA+B;QAIbT,MAAAA;IAFlB,MAAMU,MAAMH,SAASI,IAAI;IACzB,IAAI,CAACD,KAAK,MAAM,IAAIE,MAAM;IAC1B,MAAMC,WAAW,EAACb,QAAAA,0BAAAA,oCAAAA,0BAA4BS,2BAAAA,QAASI,QAAQ,YAA7Cb,OAAiD,IAAIW,IAAI;IAC3E,MAAMG,YAAYX,sBAAsBC,wBAAwBW,IAAI,CAClE,CAACC,QAAUA,MAAMN,GAAG,KAAKA;IAE3B,IAAII,aAAaD,YAAYC,UAAUD,QAAQ,KAAKA,UAAU;QAC5D,MAAM,IAAID,MACR,CAAC,UAAU,EAAEF,IAAI,mCAAmC,EAAEI,UAAUD,QAAQ,CAAC,GAAG,CAAC,GAC3E,CAAC,SAAS,EAAEA,SAAS,CAAC,CAAC;IAE7B;IACAX,sBAAsBE,wBAAwBI,QAAQ,aAChDC,CAAAA,2BAAAA,QAASI,QAAQ,IAAG;QAAEA,UAAUJ,QAAQI,QAAQ;IAAC,IAAI,CAAC;QAC1DH;;AAEJ;AAQA,mFAAmF,GACnF,OAAO,SAASO,0BAA0BV,QAAgB;IACxD,MAAMG,MAAMH,SAASI,IAAI;IACzB,MAAMK,QAAQb,sBAAsBC,wBAAwBW,IAAI,CAAC,CAACG,MAAQA,IAAIR,GAAG,KAAKA;IACtF,OAAOM,QAAQ;QAAEH,UAAUG,MAAMH,QAAQ;QAAEL,QAAQQ,MAAMG,IAAI;IAAC,IAAI;AACpE"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { PluginRevocation } from '../app-utils/plugin-manifest';
18
+ /**
19
+ * The platform's kill switch, read by a plugin that is not the one that keeps
20
+ * it (AGL-3080).
21
+ *
22
+ * An artifact installed from a distribution channel — an email design, a
23
+ * component, a plugin — carries the listing it came from on its install
24
+ * stamp (`app-utils/artifact-provenance.ts`). When the platform pulls that
25
+ * listing, or one of its versions, the revocation (`PluginRevocation`, the
26
+ * platform's own shape) is what every reader consults: the loader before it
27
+ * runs a bundle, the send path before it mails a design that was installed
28
+ * rather than written. The revocations are KEPT by the plugin that runs the
29
+ * distribution channel, and a reader that is not that plugin asks through
30
+ * this slot rather than reading its storage.
31
+ *
32
+ * ## Absent, and failed, are different answers
33
+ *
34
+ * No reader registered means nothing in this process distributes artifacts,
35
+ * so nothing here could have been installed from a listing and there is
36
+ * nothing to revoke: {@link readListingRevocation} answers `null` — after
37
+ * running the app's boot step once, because a boot whose declarations
38
+ * failed looks the same from here, and a kill switch that silently stopped
39
+ * answering is the one failure it may not have. A reader
40
+ * that FAILS throws through to the caller, which decides — a send that cannot
41
+ * learn whether its design was pulled refuses rather than mails it, the
42
+ * direction the read had before it was a seam.
43
+ *
44
+ * Server-side: reached by its own subpath, never through
45
+ * `plugin-manager/index.ts`.
46
+ */
47
+ export interface PluginRevocationReader {
48
+ /** The listing's revocation, or `null` when it was never revoked. */
49
+ revocation(listingId: string): Promise<PluginRevocation | null>;
50
+ }
51
+ export declare const PLUGIN_REVOCATIONS: import("./plugin-services").PluginServiceContract<PluginRevocationReader>;
52
+ /**
53
+ * Registers the plugin that keeps the revocations. The owner is the loader's
54
+ * marker when a register fn is running, else `options.pluginId`; a second
55
+ * plugin's reader is refused naming both.
56
+ */
57
+ export declare function registerPluginRevocationReader(reader: PluginRevocationReader, options?: {
58
+ pluginId?: string;
59
+ }): void;
60
+ /**
61
+ * A listing's revocation: `null` when it has none, or when nothing in this
62
+ * process keeps revocations. Throws what a failing reader throws.
63
+ */
64
+ export declare function readListingRevocation(listingId: string): Promise<PluginRevocation | null>;
@@ -0,0 +1,47 @@
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
+ */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
+ import { runPluginDeclarationsRepair } from "./plugin-declarations-repair.js";
18
+ import { definePluginServiceContract, registerPluginService, resolvePluginService } from "./plugin-services.js";
19
+ export const PLUGIN_REVOCATIONS = definePluginServiceContract('core.revocations', {
20
+ multiple: false
21
+ });
22
+ /**
23
+ * Registers the plugin that keeps the revocations. The owner is the loader's
24
+ * marker when a register fn is running, else `options.pluginId`; a second
25
+ * plugin's reader is refused naming both.
26
+ */ export function registerPluginRevocationReader(reader, options) {
27
+ registerPluginService(PLUGIN_REVOCATIONS, reader, _extends({}, (options == null ? void 0 : options.pluginId) ? {
28
+ pluginId: options.pluginId
29
+ } : {}));
30
+ }
31
+ /**
32
+ * A listing's revocation: `null` when it has none, or when nothing in this
33
+ * process keeps revocations. Throws what a failing reader throws.
34
+ */ export async function readListingRevocation(listingId) {
35
+ const id = String(listingId != null ? listingId : '').trim();
36
+ if (!id) return null;
37
+ let reader = resolvePluginService(PLUGIN_REVOCATIONS);
38
+ if (!reader) {
39
+ await runPluginDeclarationsRepair().catch((error)=>{
40
+ console.error('[revocations] plugin declarations failed', error);
41
+ });
42
+ reader = resolvePluginService(PLUGIN_REVOCATIONS);
43
+ }
44
+ return reader ? reader.revocation(id) : null;
45
+ }
46
+
47
+ //# sourceMappingURL=plugin-revocations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-revocations.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 { PluginRevocation } from '../app-utils/plugin-manifest'\nimport { runPluginDeclarationsRepair } from './plugin-declarations-repair'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginService,\n} from './plugin-services'\n\n/**\n * The platform's kill switch, read by a plugin that is not the one that keeps\n * it (AGL-3080).\n *\n * An artifact installed from a distribution channel — an email design, a\n * component, a plugin — carries the listing it came from on its install\n * stamp (`app-utils/artifact-provenance.ts`). When the platform pulls that\n * listing, or one of its versions, the revocation (`PluginRevocation`, the\n * platform's own shape) is what every reader consults: the loader before it\n * runs a bundle, the send path before it mails a design that was installed\n * rather than written. The revocations are KEPT by the plugin that runs the\n * distribution channel, and a reader that is not that plugin asks through\n * this slot rather than reading its storage.\n *\n * ## Absent, and failed, are different answers\n *\n * No reader registered means nothing in this process distributes artifacts,\n * so nothing here could have been installed from a listing and there is\n * nothing to revoke: {@link readListingRevocation} answers `null` — after\n * running the app's boot step once, because a boot whose declarations\n * failed looks the same from here, and a kill switch that silently stopped\n * answering is the one failure it may not have. A reader\n * that FAILS throws through to the caller, which decides — a send that cannot\n * learn whether its design was pulled refuses rather than mails it, the\n * direction the read had before it was a seam.\n *\n * Server-side: reached by its own subpath, never through\n * `plugin-manager/index.ts`.\n */\n\nexport interface PluginRevocationReader {\n /** The listing's revocation, or `null` when it was never revoked. */\n revocation(listingId: string): Promise<PluginRevocation | null>\n}\n\nexport const PLUGIN_REVOCATIONS = definePluginServiceContract<PluginRevocationReader>(\n 'core.revocations',\n { multiple: false },\n)\n\n/**\n * Registers the plugin that keeps the revocations. The owner is the loader's\n * marker when a register fn is running, else `options.pluginId`; a second\n * plugin's reader is refused naming both.\n */\nexport function registerPluginRevocationReader(\n reader: PluginRevocationReader,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_REVOCATIONS, reader, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\n/**\n * A listing's revocation: `null` when it has none, or when nothing in this\n * process keeps revocations. Throws what a failing reader throws.\n */\nexport async function readListingRevocation(listingId: string): Promise<PluginRevocation | null> {\n const id = String(listingId ?? '').trim()\n if (!id) return null\n let reader = resolvePluginService(PLUGIN_REVOCATIONS)\n if (!reader) {\n await runPluginDeclarationsRepair().catch((error: unknown) => {\n console.error('[revocations] plugin declarations failed', error)\n })\n reader = resolvePluginService(PLUGIN_REVOCATIONS)\n }\n return reader ? reader.revocation(id) : null\n}\n"],"names":["runPluginDeclarationsRepair","definePluginServiceContract","registerPluginService","resolvePluginService","PLUGIN_REVOCATIONS","multiple","registerPluginRevocationReader","reader","options","pluginId","readListingRevocation","listingId","id","String","trim","catch","error","console","revocation"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,2BAA2B,QAAQ,kCAA8B;AAC1E,SACEC,2BAA2B,EAC3BC,qBAAqB,EACrBC,oBAAoB,QACf,uBAAmB;AAqC1B,OAAO,MAAMC,qBAAqBH,4BAChC,oBACA;IAAEI,UAAU;AAAM,GACnB;AAED;;;;CAIC,GACD,OAAO,SAASC,+BACdC,MAA8B,EAC9BC,OAA+B;IAE/BN,sBAAsBE,oBAAoBG,QAAQ,aAC5CC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAEA;;;CAGC,GACD,OAAO,eAAeC,sBAAsBC,SAAiB;IAC3D,MAAMC,KAAKC,OAAOF,oBAAAA,YAAa,IAAIG,IAAI;IACvC,IAAI,CAACF,IAAI,OAAO;IAChB,IAAIL,SAASJ,qBAAqBC;IAClC,IAAI,CAACG,QAAQ;QACX,MAAMP,8BAA8Be,KAAK,CAAC,CAACC;YACzCC,QAAQD,KAAK,CAAC,4CAA4CA;QAC5D;QACAT,SAASJ,qBAAqBC;IAChC;IACA,OAAOG,SAASA,OAAOW,UAAU,CAACN,MAAM;AAC1C"}
@@ -0,0 +1,69 @@
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
+ * A bulk send's own figures, moved by a door that is not its sender
19
+ * (AGL-3080).
20
+ *
21
+ * A message a bulk send carried names the send — its id is signed into the
22
+ * unsubscribe link and tagged on the message — and the plugin that serves
23
+ * the site's unsubscribe page learns that a recipient left. That plugin is
24
+ * not the sender: the send, and the figures its report reads, are the
25
+ * plugin's that sent it. So the door reports what happened and the sender
26
+ * counts it, without either importing the other.
27
+ *
28
+ * A MULTIPLE contract: several plugins may send in bulk, and the door does
29
+ * not know which one a send id is. Each tally answers whether the send was
30
+ * its own and counted; the first that answers yes ends the ask.
31
+ *
32
+ * ## The caller proves what happened
33
+ *
34
+ * The registry authenticates nobody. A door counts only what it has already
35
+ * written: an unsubscribe is counted once, when its suppression was created,
36
+ * never on a second click of the same link.
37
+ *
38
+ * Server-side: reached by its own subpath, never through
39
+ * `plugin-manager/index.ts`.
40
+ */
41
+ /** A send, as a link or a message tag names it. */
42
+ export interface PluginSendTallyRequest {
43
+ /** The site whose mail it was. */
44
+ hostId: string;
45
+ /** The send's id, as signed into the link. */
46
+ sendId: string;
47
+ }
48
+ export interface PluginSendTally {
49
+ /**
50
+ * One more recipient left from this send's mail. Answers whether the send
51
+ * is this plugin's and was counted; a send it does not keep is `false`.
52
+ */
53
+ unsubscribed(request: PluginSendTallyRequest): Promise<boolean>;
54
+ }
55
+ export declare const PLUGIN_SEND_TALLIES: import("./plugin-services").PluginServiceContract<PluginSendTally>;
56
+ /**
57
+ * Registers a sender's tally. The owner is the loader's marker when a
58
+ * register fn is running, else `options.pluginId`; registering again under
59
+ * the same plugin replaces in place.
60
+ */
61
+ export declare function registerPluginSendTally(tally: PluginSendTally, options?: {
62
+ pluginId?: string;
63
+ }): void;
64
+ /**
65
+ * Tells the sender of a send that a recipient left. Never throws: the door's
66
+ * own write — the suppression — has landed, and a figure must never be able
67
+ * to cost it. Answers whether any sender counted it.
68
+ */
69
+ export declare function tallySendUnsubscribe(request: PluginSendTallyRequest): Promise<boolean>;
@@ -0,0 +1,45 @@
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_SEND_TALLIES = definePluginServiceContract('core.send-tallies', {
19
+ multiple: true
20
+ });
21
+ /**
22
+ * Registers a sender's tally. The owner is the loader's marker when a
23
+ * register fn is running, else `options.pluginId`; registering again under
24
+ * the same plugin replaces in place.
25
+ */ export function registerPluginSendTally(tally, options) {
26
+ registerPluginService(PLUGIN_SEND_TALLIES, tally, _extends({}, (options == null ? void 0 : options.pluginId) ? {
27
+ pluginId: options.pluginId
28
+ } : {}));
29
+ }
30
+ /**
31
+ * Tells the sender of a send that a recipient left. Never throws: the door's
32
+ * own write — the suppression — has landed, and a figure must never be able
33
+ * to cost it. Answers whether any sender counted it.
34
+ */ export async function tallySendUnsubscribe(request) {
35
+ for (const entry of resolvePluginServices(PLUGIN_SEND_TALLIES)){
36
+ try {
37
+ if (await entry.impl.unsubscribed(request)) return true;
38
+ } catch (error) {
39
+ console.error(`[send-tallies] ${entry.pluginId} failed on ${request.hostId}`, error);
40
+ }
41
+ }
42
+ return false;
43
+ }
44
+
45
+ //# sourceMappingURL=plugin-send-tallies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-send-tallies.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 * A bulk send's own figures, moved by a door that is not its sender\n * (AGL-3080).\n *\n * A message a bulk send carried names the send — its id is signed into the\n * unsubscribe link and tagged on the message — and the plugin that serves\n * the site's unsubscribe page learns that a recipient left. That plugin is\n * not the sender: the send, and the figures its report reads, are the\n * plugin's that sent it. So the door reports what happened and the sender\n * counts it, without either importing the other.\n *\n * A MULTIPLE contract: several plugins may send in bulk, and the door does\n * not know which one a send id is. Each tally answers whether the send was\n * its own and counted; the first that answers yes ends the ask.\n *\n * ## The caller proves what happened\n *\n * The registry authenticates nobody. A door counts only what it has already\n * written: an unsubscribe is counted once, when its suppression was created,\n * never on a second click of the same link.\n *\n * Server-side: reached by its own subpath, never through\n * `plugin-manager/index.ts`.\n */\n\n/** A send, as a link or a message tag names it. */\nexport interface PluginSendTallyRequest {\n /** The site whose mail it was. */\n hostId: string\n /** The send's id, as signed into the link. */\n sendId: string\n}\n\nexport interface PluginSendTally {\n /**\n * One more recipient left from this send's mail. Answers whether the send\n * is this plugin's and was counted; a send it does not keep is `false`.\n */\n unsubscribed(request: PluginSendTallyRequest): Promise<boolean>\n}\n\nexport const PLUGIN_SEND_TALLIES = definePluginServiceContract<PluginSendTally>(\n 'core.send-tallies',\n { multiple: true },\n)\n\n/**\n * Registers a sender's tally. The owner is the loader's marker when a\n * register fn is running, else `options.pluginId`; registering again under\n * the same plugin replaces in place.\n */\nexport function registerPluginSendTally(\n tally: PluginSendTally,\n options?: { pluginId?: string },\n): void {\n registerPluginService(PLUGIN_SEND_TALLIES, tally, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n })\n}\n\n/**\n * Tells the sender of a send that a recipient left. Never throws: the door's\n * own write — the suppression — has landed, and a figure must never be able\n * to cost it. Answers whether any sender counted it.\n */\nexport async function tallySendUnsubscribe(request: PluginSendTallyRequest): Promise<boolean> {\n for (const entry of resolvePluginServices(PLUGIN_SEND_TALLIES)) {\n try {\n if (await entry.impl.unsubscribed(request)) return true\n } catch (error) {\n console.error(`[send-tallies] ${entry.pluginId} failed on ${request.hostId}`, error)\n }\n }\n return false\n}\n"],"names":["definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_SEND_TALLIES","multiple","registerPluginSendTally","tally","options","pluginId","tallySendUnsubscribe","request","entry","impl","unsubscribed","error","console","hostId"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AA2C1B,OAAO,MAAMC,sBAAsBH,4BACjC,qBACA;IAAEI,UAAU;AAAK,GAClB;AAED;;;;CAIC,GACD,OAAO,SAASC,wBACdC,KAAsB,EACtBC,OAA+B;IAE/BN,sBAAsBE,qBAAqBG,OAAO,aAC5CC,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;QAAEA,UAAUD,QAAQC,QAAQ;IAAC,IAAI,CAAC;AAE9D;AAEA;;;;CAIC,GACD,OAAO,eAAeC,qBAAqBC,OAA+B;IACxE,KAAK,MAAMC,SAAST,sBAAsBC,qBAAsB;QAC9D,IAAI;YACF,IAAI,MAAMQ,MAAMC,IAAI,CAACC,YAAY,CAACH,UAAU,OAAO;QACrD,EAAE,OAAOI,OAAO;YACdC,QAAQD,KAAK,CAAC,CAAC,eAAe,EAAEH,MAAMH,QAAQ,CAAC,WAAW,EAAEE,QAAQM,MAAM,EAAE,EAAEF;QAChF;IACF;IACA,OAAO;AACT"}
@@ -61,6 +61,23 @@ export interface PluginSiteBundleSectionDeclaration {
61
61
  key: string;
62
62
  /** The most items one bundle carries; a restore reads no more. */
63
63
  limit: number;
64
+ /**
65
+ * What a site package calls each item (AGL-3533): its kind, the plural
66
+ * label the import screen groups them under, and the field an item with a
67
+ * new id is matched by. Its references are the plugin's to name, through
68
+ * the section's registered {@link PluginSiteBundleSection.package} hooks.
69
+ */
70
+ package: {
71
+ kind: string;
72
+ label: string;
73
+ nameField?: string;
74
+ slugField?: string;
75
+ /** Node props that place one of its items in a design, as a host collection's declare them. */
76
+ placements?: ReadonlyArray<{
77
+ componentId?: string;
78
+ prop: string;
79
+ }>;
80
+ };
64
81
  }
65
82
  /** A declaration with the plugin that made it. */
66
83
  export type ResolvedPluginSiteBundleSectionDeclaration = PluginSiteBundleSectionDeclaration & {
@@ -104,8 +121,31 @@ export interface SiteBundleImportRequest extends SiteBundleRestoreRequest {
104
121
  }
105
122
  /** A row the restore reports without refusing: a document restored as it was, and why it is worth a look. */
106
123
  export type SiteBundleReportRow = Record<string, unknown>;
124
+ /** An item another item names: its package kind and id. */
125
+ export interface SiteBundleItemReference {
126
+ kind: string;
127
+ id: string;
128
+ }
129
+ /**
130
+ * A section's answers about its items as package items (AGL-3533): what each
131
+ * one refers to — another of its own items, a page, a media file — and the
132
+ * item with those references moved to new ids, for an item kept beside an
133
+ * existing one or mapped onto one the site already holds.
134
+ */
135
+ export interface PluginSiteBundleSectionPackage {
136
+ /** The site items this item names, by package kind and id. */
137
+ dependencies(item: SiteBundleItem): SiteBundleItemReference[];
138
+ /**
139
+ * The item with every reference in `idMap` moved: `idMap` maps an item key
140
+ * (`<kind>/<id>`) to the id it now has, or to `null` when the reference is
141
+ * dropped. The item's own `$id` is the restore's to set, not this hook's.
142
+ */
143
+ remapIds(item: SiteBundleItem, idMap: ReadonlyMap<string, string | null>): SiteBundleItem;
144
+ }
107
145
  /** A plugin's answers for one section. */
108
146
  export interface PluginSiteBundleSection {
147
+ /** Its items as package items; required, since every section declares a package kind. */
148
+ package: PluginSiteBundleSectionPackage;
109
149
  /** The site's share, read whole or not at all: throw rather than return a short list. */
110
150
  export(request: SiteBundleRequest): Promise<SiteBundleItem[]>;
111
151
  /**
@@ -38,6 +38,7 @@ const sections = (_globalScope_SECTIONS_KEY = globalScope[SECTIONS_KEY]) != null
38
38
  * replaces its answers.
39
39
  */ export function registerPluginSiteBundleSection(key, section, options) {
40
40
  var _ref, _getRegisteringPluginId;
41
+ var _section_package, _section_package1;
41
42
  const name = key.trim();
42
43
  const pluginId = ((_ref = (_getRegisteringPluginId = getRegisteringPluginId()) != null ? _getRegisteringPluginId : options == null ? void 0 : options.pluginId) != null ? _ref : '').trim();
43
44
  if (!pluginId) {
@@ -50,6 +51,9 @@ const sections = (_globalScope_SECTIONS_KEY = globalScope[SECTIONS_KEY]) != null
50
51
  if (declared.pluginId !== pluginId) {
51
52
  throw new Error(`site bundle section "${name}" is declared by "${declared.pluginId}"; ` + `refused "${pluginId}"`);
52
53
  }
54
+ if (typeof ((_section_package = section.package) == null ? void 0 : _section_package.dependencies) !== 'function' || typeof ((_section_package1 = section.package) == null ? void 0 : _section_package1.remapIds) !== 'function') {
55
+ throw new Error(`site bundle section "${name}" declares package kind "${declared.package.kind}" ` + 'and registers no package hooks (dependencies, remapIds), so a package ' + 'could neither list what its items need nor keep a copy beside an existing one');
56
+ }
53
57
  sections.set(name, {
54
58
  pluginId,
55
59
  section
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-site-bundle.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 { PLUGIN_SITE_BUNDLE_SECTIONS_DECLARED } from './first-party-plugins.generated'\nimport { runPluginDeclarationsRepair } from './plugin-declarations-repair'\n\n/**\n * A plugin's SECTION of the whole-site backup (AGL-3080): what it carries in\n * the bundle and restores from it, when a host collection's `siteExport`\n * cannot say so.\n *\n * A plain host collection is copied by the export and restore routes\n * themselves (`plugin-site-export.ts`). Some plugin data is not that shape:\n * it lives under the ORGANIZATION and is narrowed to the site being backed up,\n * it carries a subcollection, a restore has to validate it against its own\n * model and derive what the model implies, and its count is sold as an add-on\n * rather than read off the plan. Only the plugin knows any of that. So the\n * plugin declares the section and answers for it: the export asks it for the\n * site's share, and the restore asks it — before the first write — whether\n * the bundle would cross what the organization has paid for, then hands it the\n * bundle's items and the restore's writer.\n *\n * ## Declared, then registered — and an absent section is refused\n *\n * The plugin declares the section in `plugins.config.json`\n * (`siteBundleSections`: the bundle key and the most items it carries), which\n * the generator compiles, and registers the answer from its\n * `serverDeclarations` entry. A backup that silently lacks a section is worse\n * than a failed one — the file looks whole until the day it is restored — so\n * a section declared and not registered is not read as \"nothing to carry\":\n * {@link resolveSiteBundleSections} runs the app's declarations step again,\n * and throws if the section is still missing, which fails the export or the\n * restore out loud.\n *\n * ## What the restore owns\n *\n * The section writes through {@link SiteBundleImportRequest.write}, never\n * with a writer of its own: the restore's chunked batches, its document count\n * and its all-or-nothing refusal are the restore's, and a write that went\n * around them would land after a refusal or be missing from the total. The\n * restore's own stamps (`createdAt`, `updatedAt`) come from\n * {@link SiteBundleImportRequest.stamps}, so a restored document is dated the\n * way every other one is.\n *\n * Reached by its own subpath, never the barrel: its readers are the two\n * console routes and the plugins that answer them.\n */\n\n/** A section as declared: its key in the bundle and the most items it carries. */\nexport interface PluginSiteBundleSectionDeclaration {\n /** The bundle's top-level key for this section's items, e.g. `datasets`. */\n key: string\n /** The most items one bundle carries; a restore reads no more. */\n limit: number\n}\n\n/** A declaration with the plugin that made it. */\nexport type ResolvedPluginSiteBundleSectionDeclaration =\n PluginSiteBundleSectionDeclaration & { pluginId: string }\n\n/** One item of a section, as the bundle carries it: a document with its `$id`. */\nexport type SiteBundleItem = Record<string, any>\n\n/** Whose backup this is. */\nexport interface SiteBundleRequest {\n hostId: string\n /**\n * The site's organization, or `null` for a site with none — which holds no\n * organization data, a known-empty answer rather than a failure.\n */\n orgId: string | null\n /** The section's declared limit: the most items one bundle carries. */\n limit: number\n}\n\n/** The restore's question: this bundle's items, for this site. */\nexport interface SiteBundleRestoreRequest extends SiteBundleRequest {\n /** The organization's document, for the plan and add-ons a count is met against. */\n org: unknown\n /** The bundle's items under the section's key, already capped at its declared limit. */\n items: readonly SiteBundleItem[]\n}\n\n/** What the restore hands a section to write with. */\nexport interface SiteBundleImportRequest extends SiteBundleRestoreRequest {\n /**\n * Writes one document, whole (`merge: false`), by its full path\n * (`orgs/{orgId}/…`), on the restore's batches and counted in its total.\n */\n write(documentPath: string, data: Record<string, unknown>): Promise<void>\n /** The restore's own stamps for a document it writes: its creation and update times. */\n stamps(): Record<string, unknown>\n /**\n * Loads every plugin's console server surface, for a section that checks\n * what it restores against something other plugins register there (a\n * custom field type). Costs a load, so a section asks only when it needs to.\n */\n loadPluginSurfaces(): Promise<void>\n}\n\n/** A row the restore reports without refusing: a document restored as it was, and why it is worth a look. */\nexport type SiteBundleReportRow = Record<string, unknown>\n\n/** A plugin's answers for one section. */\nexport interface PluginSiteBundleSection {\n /** The site's share, read whole or not at all: throw rather than return a short list. */\n export(request: SiteBundleRequest): Promise<SiteBundleItem[]>\n /**\n * Before the restore writes anything: the sentence it answers 403 with when\n * restoring these items would cross what the organization may hold, or\n * `null`. A bundle refused here writes nothing at all.\n */\n refusal?(request: SiteBundleRestoreRequest): Promise<string | null>\n /** Writes the items back through `request.write`; answers the rows worth reporting. */\n import(request: SiteBundleImportRequest): Promise<readonly SiteBundleReportRow[]>\n}\n\ninterface Registered {\n pluginId: string\n section: PluginSiteBundleSection\n}\n\n/**\n * One table per process, on `globalThis` (AGL-3412): a plugin registers from\n * its declarations at boot, which Next compiles apart from the routes that\n * read, and a module-scoped map would be filled in one copy and read empty in\n * the other.\n */\nconst SECTIONS_KEY = Symbol.for('@aglyn/aglyn:site-bundle-sections')\n\nconst globalScope = globalThis as typeof globalThis & {\n [SECTIONS_KEY]?: Map<string, Registered>\n}\n\nconst sections: Map<string, Registered> =\n globalScope[SECTIONS_KEY] ?? (globalScope[SECTIONS_KEY] = new Map())\n\n/** Every declared section, in config order — the order the bundle carries them. */\nexport function listDeclaredSiteBundleSections(): readonly ResolvedPluginSiteBundleSectionDeclaration[] {\n return PLUGIN_SITE_BUNDLE_SECTIONS_DECLARED\n}\n\n/**\n * Registers a plugin's answers for a section it declared. The owner is the\n * plugin whose register fn is running, or the `pluginId` passed from a boot\n * declaration. Refused: no owner, a key nobody declared (only a declared\n * section is ever read, so an undeclared one would be carried by nothing),\n * and a key another plugin declared. The same plugin registering again\n * replaces its answers.\n */\nexport function registerPluginSiteBundleSection(\n key: string,\n section: PluginSiteBundleSection,\n options?: { pluginId?: string },\n): void {\n const name = key.trim()\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n if (!pluginId) {\n throw new Error(\n `site bundle section \"${name}\" registered with no owner: pass { pluginId } ` +\n 'when registering outside a plugin register fn',\n )\n }\n const declared = listDeclaredSiteBundleSections().find((one) => one.key === name)\n if (!declared) {\n throw new Error(\n `site bundle section \"${name}\" is not declared in plugins.config.json ` +\n '(siteBundleSections), so no export would ever carry it',\n )\n }\n if (declared.pluginId !== pluginId) {\n throw new Error(\n `site bundle section \"${name}\" is declared by \"${declared.pluginId}\"; ` +\n `refused \"${pluginId}\"`,\n )\n }\n sections.set(name, { pluginId, section })\n}\n\n/** A declared section with the answers its plugin registered. */\nexport type ResolvedSiteBundleSection = ResolvedPluginSiteBundleSectionDeclaration & {\n section: PluginSiteBundleSection\n}\n\n/**\n * Every declared section with its answers, in config order. A declared\n * section with nothing registered runs the app's declarations step once and\n * asks again; still missing, this THROWS naming it — an export that carried\n * the rest would be a backup missing a section with nothing to say so.\n */\nexport async function resolveSiteBundleSections(): Promise<ResolvedSiteBundleSection[]> {\n const missing = () =>\n listDeclaredSiteBundleSections().filter((declared) => !sections.has(declared.key))\n if (missing().length) await runPluginDeclarationsRepair()\n const absent = missing()\n if (absent.length) {\n throw new Error(\n `[plugins] site bundle section(s) ${absent.map((one) => `\"${one.key}\" (${one.pluginId})`).join(', ')} ` +\n 'declared and not registered in this process',\n )\n }\n return listDeclaredSiteBundleSections().map((declared) => ({\n ...declared,\n section: (sections.get(declared.key) as Registered).section,\n }))\n}\n\n/** Only for specs: forgets every registered section. */\nexport function resetSiteBundleSectionsForTests(): void {\n sections.clear()\n}\n"],"names":["globalScope","getRegisteringPluginId","PLUGIN_SITE_BUNDLE_SECTIONS_DECLARED","runPluginDeclarationsRepair","SECTIONS_KEY","Symbol","for","globalThis","sections","Map","listDeclaredSiteBundleSections","registerPluginSiteBundleSection","key","section","options","name","trim","pluginId","Error","declared","find","one","set","resolveSiteBundleSections","missing","filter","has","length","absent","map","join","get","resetSiteBundleSectionsForTests","clear"],"mappings":";IAsJEA;AAtJF;;;;;;;;;;;;;;;CAeC,GAED,SAASC,sBAAsB,QAAQ,qCAAiC;AACxE,SAASC,oCAAoC,QAAQ,qCAAiC;AACtF,SAASC,2BAA2B,QAAQ,kCAA8B;AAsH1E;;;;;CAKC,GACD,MAAMC,eAAeC,OAAOC,GAAG,CAAC;AAEhC,MAAMN,cAAcO;AAIpB,MAAMC,YACJR,4BAAAA,WAAW,CAACI,aAAa,YAAzBJ,4BAA8BA,WAAW,CAACI,aAAa,GAAG,IAAIK;AAEhE,iFAAiF,GACjF,OAAO,SAASC;IACd,OAAOR;AACT;AAEA;;;;;;;CAOC,GACD,OAAO,SAASS,gCACdC,GAAW,EACXC,OAAgC,EAChCC,OAA+B;QAGbb,MAAAA;IADlB,MAAMc,OAAOH,IAAII,IAAI;IACrB,MAAMC,WAAW,EAAChB,QAAAA,0BAAAA,oCAAAA,0BAA4Ba,2BAAAA,QAASG,QAAQ,YAA7ChB,OAAiD,IAAIe,IAAI;IAC3E,IAAI,CAACC,UAAU;QACb,MAAM,IAAIC,MACR,CAAC,qBAAqB,EAAEH,KAAK,8CAA8C,CAAC,GAC1E;IAEN;IACA,MAAMI,WAAWT,iCAAiCU,IAAI,CAAC,CAACC,MAAQA,IAAIT,GAAG,KAAKG;IAC5E,IAAI,CAACI,UAAU;QACb,MAAM,IAAID,MACR,CAAC,qBAAqB,EAAEH,KAAK,yCAAyC,CAAC,GACrE;IAEN;IACA,IAAII,SAASF,QAAQ,KAAKA,UAAU;QAClC,MAAM,IAAIC,MACR,CAAC,qBAAqB,EAAEH,KAAK,kBAAkB,EAAEI,SAASF,QAAQ,CAAC,GAAG,CAAC,GACrE,CAAC,SAAS,EAAEA,SAAS,CAAC,CAAC;IAE7B;IACAT,SAASc,GAAG,CAACP,MAAM;QAAEE;QAAUJ;IAAQ;AACzC;AAOA;;;;;CAKC,GACD,OAAO,eAAeU;IACpB,MAAMC,UAAU,IACdd,iCAAiCe,MAAM,CAAC,CAACN,WAAa,CAACX,SAASkB,GAAG,CAACP,SAASP,GAAG;IAClF,IAAIY,UAAUG,MAAM,EAAE,MAAMxB;IAC5B,MAAMyB,SAASJ;IACf,IAAII,OAAOD,MAAM,EAAE;QACjB,MAAM,IAAIT,MACR,CAAC,iCAAiC,EAAEU,OAAOC,GAAG,CAAC,CAACR,MAAQ,CAAC,CAAC,EAAEA,IAAIT,GAAG,CAAC,GAAG,EAAES,IAAIJ,QAAQ,CAAC,CAAC,CAAC,EAAEa,IAAI,CAAC,MAAM,CAAC,CAAC,GACrG;IAEN;IACA,OAAOpB,iCAAiCmB,GAAG,CAAC,CAACV,WAAc,aACtDA;YACHN,SAAS,AAACL,SAASuB,GAAG,CAACZ,SAASP,GAAG,EAAiBC,OAAO;;AAE/D;AAEA,sDAAsD,GACtD,OAAO,SAASmB;IACdxB,SAASyB,KAAK;AAChB"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-site-bundle.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 { PLUGIN_SITE_BUNDLE_SECTIONS_DECLARED } from './first-party-plugins.generated'\nimport { runPluginDeclarationsRepair } from './plugin-declarations-repair'\n\n/**\n * A plugin's SECTION of the whole-site backup (AGL-3080): what it carries in\n * the bundle and restores from it, when a host collection's `siteExport`\n * cannot say so.\n *\n * A plain host collection is copied by the export and restore routes\n * themselves (`plugin-site-export.ts`). Some plugin data is not that shape:\n * it lives under the ORGANIZATION and is narrowed to the site being backed up,\n * it carries a subcollection, a restore has to validate it against its own\n * model and derive what the model implies, and its count is sold as an add-on\n * rather than read off the plan. Only the plugin knows any of that. So the\n * plugin declares the section and answers for it: the export asks it for the\n * site's share, and the restore asks it — before the first write — whether\n * the bundle would cross what the organization has paid for, then hands it the\n * bundle's items and the restore's writer.\n *\n * ## Declared, then registered — and an absent section is refused\n *\n * The plugin declares the section in `plugins.config.json`\n * (`siteBundleSections`: the bundle key and the most items it carries), which\n * the generator compiles, and registers the answer from its\n * `serverDeclarations` entry. A backup that silently lacks a section is worse\n * than a failed one — the file looks whole until the day it is restored — so\n * a section declared and not registered is not read as \"nothing to carry\":\n * {@link resolveSiteBundleSections} runs the app's declarations step again,\n * and throws if the section is still missing, which fails the export or the\n * restore out loud.\n *\n * ## What the restore owns\n *\n * The section writes through {@link SiteBundleImportRequest.write}, never\n * with a writer of its own: the restore's chunked batches, its document count\n * and its all-or-nothing refusal are the restore's, and a write that went\n * around them would land after a refusal or be missing from the total. The\n * restore's own stamps (`createdAt`, `updatedAt`) come from\n * {@link SiteBundleImportRequest.stamps}, so a restored document is dated the\n * way every other one is.\n *\n * Reached by its own subpath, never the barrel: its readers are the two\n * console routes and the plugins that answer them.\n */\n\n/** A section as declared: its key in the bundle and the most items it carries. */\nexport interface PluginSiteBundleSectionDeclaration {\n /** The bundle's top-level key for this section's items, e.g. `datasets`. */\n key: string\n /** The most items one bundle carries; a restore reads no more. */\n limit: number\n /**\n * What a site package calls each item (AGL-3533): its kind, the plural\n * label the import screen groups them under, and the field an item with a\n * new id is matched by. Its references are the plugin's to name, through\n * the section's registered {@link PluginSiteBundleSection.package} hooks.\n */\n package: {\n kind: string\n label: string\n nameField?: string\n slugField?: string\n /** Node props that place one of its items in a design, as a host collection's declare them. */\n placements?: ReadonlyArray<{ componentId?: string; prop: string }>\n }\n}\n\n/** A declaration with the plugin that made it. */\nexport type ResolvedPluginSiteBundleSectionDeclaration =\n PluginSiteBundleSectionDeclaration & { pluginId: string }\n\n/** One item of a section, as the bundle carries it: a document with its `$id`. */\nexport type SiteBundleItem = Record<string, any>\n\n/** Whose backup this is. */\nexport interface SiteBundleRequest {\n hostId: string\n /**\n * The site's organization, or `null` for a site with none — which holds no\n * organization data, a known-empty answer rather than a failure.\n */\n orgId: string | null\n /** The section's declared limit: the most items one bundle carries. */\n limit: number\n}\n\n/** The restore's question: this bundle's items, for this site. */\nexport interface SiteBundleRestoreRequest extends SiteBundleRequest {\n /** The organization's document, for the plan and add-ons a count is met against. */\n org: unknown\n /** The bundle's items under the section's key, already capped at its declared limit. */\n items: readonly SiteBundleItem[]\n}\n\n/** What the restore hands a section to write with. */\nexport interface SiteBundleImportRequest extends SiteBundleRestoreRequest {\n /**\n * Writes one document, whole (`merge: false`), by its full path\n * (`orgs/{orgId}/…`), on the restore's batches and counted in its total.\n */\n write(documentPath: string, data: Record<string, unknown>): Promise<void>\n /** The restore's own stamps for a document it writes: its creation and update times. */\n stamps(): Record<string, unknown>\n /**\n * Loads every plugin's console server surface, for a section that checks\n * what it restores against something other plugins register there (a\n * custom field type). Costs a load, so a section asks only when it needs to.\n */\n loadPluginSurfaces(): Promise<void>\n}\n\n/** A row the restore reports without refusing: a document restored as it was, and why it is worth a look. */\nexport type SiteBundleReportRow = Record<string, unknown>\n\n/** An item another item names: its package kind and id. */\nexport interface SiteBundleItemReference {\n kind: string\n id: string\n}\n\n/**\n * A section's answers about its items as package items (AGL-3533): what each\n * one refers to — another of its own items, a page, a media file — and the\n * item with those references moved to new ids, for an item kept beside an\n * existing one or mapped onto one the site already holds.\n */\nexport interface PluginSiteBundleSectionPackage {\n /** The site items this item names, by package kind and id. */\n dependencies(item: SiteBundleItem): SiteBundleItemReference[]\n /**\n * The item with every reference in `idMap` moved: `idMap` maps an item key\n * (`<kind>/<id>`) to the id it now has, or to `null` when the reference is\n * dropped. The item's own `$id` is the restore's to set, not this hook's.\n */\n remapIds(item: SiteBundleItem, idMap: ReadonlyMap<string, string | null>): SiteBundleItem\n}\n\n/** A plugin's answers for one section. */\nexport interface PluginSiteBundleSection {\n /** Its items as package items; required, since every section declares a package kind. */\n package: PluginSiteBundleSectionPackage\n /** The site's share, read whole or not at all: throw rather than return a short list. */\n export(request: SiteBundleRequest): Promise<SiteBundleItem[]>\n /**\n * Before the restore writes anything: the sentence it answers 403 with when\n * restoring these items would cross what the organization may hold, or\n * `null`. A bundle refused here writes nothing at all.\n */\n refusal?(request: SiteBundleRestoreRequest): Promise<string | null>\n /** Writes the items back through `request.write`; answers the rows worth reporting. */\n import(request: SiteBundleImportRequest): Promise<readonly SiteBundleReportRow[]>\n}\n\ninterface Registered {\n pluginId: string\n section: PluginSiteBundleSection\n}\n\n/**\n * One table per process, on `globalThis` (AGL-3412): a plugin registers from\n * its declarations at boot, which Next compiles apart from the routes that\n * read, and a module-scoped map would be filled in one copy and read empty in\n * the other.\n */\nconst SECTIONS_KEY = Symbol.for('@aglyn/aglyn:site-bundle-sections')\n\nconst globalScope = globalThis as typeof globalThis & {\n [SECTIONS_KEY]?: Map<string, Registered>\n}\n\nconst sections: Map<string, Registered> =\n globalScope[SECTIONS_KEY] ?? (globalScope[SECTIONS_KEY] = new Map())\n\n/** Every declared section, in config order — the order the bundle carries them. */\nexport function listDeclaredSiteBundleSections(): readonly ResolvedPluginSiteBundleSectionDeclaration[] {\n return PLUGIN_SITE_BUNDLE_SECTIONS_DECLARED\n}\n\n/**\n * Registers a plugin's answers for a section it declared. The owner is the\n * plugin whose register fn is running, or the `pluginId` passed from a boot\n * declaration. Refused: no owner, a key nobody declared (only a declared\n * section is ever read, so an undeclared one would be carried by nothing),\n * and a key another plugin declared. The same plugin registering again\n * replaces its answers.\n */\nexport function registerPluginSiteBundleSection(\n key: string,\n section: PluginSiteBundleSection,\n options?: { pluginId?: string },\n): void {\n const name = key.trim()\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n if (!pluginId) {\n throw new Error(\n `site bundle section \"${name}\" registered with no owner: pass { pluginId } ` +\n 'when registering outside a plugin register fn',\n )\n }\n const declared = listDeclaredSiteBundleSections().find((one) => one.key === name)\n if (!declared) {\n throw new Error(\n `site bundle section \"${name}\" is not declared in plugins.config.json ` +\n '(siteBundleSections), so no export would ever carry it',\n )\n }\n if (declared.pluginId !== pluginId) {\n throw new Error(\n `site bundle section \"${name}\" is declared by \"${declared.pluginId}\"; ` +\n `refused \"${pluginId}\"`,\n )\n }\n if (\n typeof section.package?.dependencies !== 'function' ||\n typeof section.package?.remapIds !== 'function'\n ) {\n throw new Error(\n `site bundle section \"${name}\" declares package kind \"${declared.package.kind}\" ` +\n 'and registers no package hooks (dependencies, remapIds), so a package ' +\n 'could neither list what its items need nor keep a copy beside an existing one',\n )\n }\n sections.set(name, { pluginId, section })\n}\n\n/** A declared section with the answers its plugin registered. */\nexport type ResolvedSiteBundleSection = ResolvedPluginSiteBundleSectionDeclaration & {\n section: PluginSiteBundleSection\n}\n\n/**\n * Every declared section with its answers, in config order. A declared\n * section with nothing registered runs the app's declarations step once and\n * asks again; still missing, this THROWS naming it — an export that carried\n * the rest would be a backup missing a section with nothing to say so.\n */\nexport async function resolveSiteBundleSections(): Promise<ResolvedSiteBundleSection[]> {\n const missing = () =>\n listDeclaredSiteBundleSections().filter((declared) => !sections.has(declared.key))\n if (missing().length) await runPluginDeclarationsRepair()\n const absent = missing()\n if (absent.length) {\n throw new Error(\n `[plugins] site bundle section(s) ${absent.map((one) => `\"${one.key}\" (${one.pluginId})`).join(', ')} ` +\n 'declared and not registered in this process',\n )\n }\n return listDeclaredSiteBundleSections().map((declared) => ({\n ...declared,\n section: (sections.get(declared.key) as Registered).section,\n }))\n}\n\n/** Only for specs: forgets every registered section. */\nexport function resetSiteBundleSectionsForTests(): void {\n sections.clear()\n}\n"],"names":["globalScope","getRegisteringPluginId","PLUGIN_SITE_BUNDLE_SECTIONS_DECLARED","runPluginDeclarationsRepair","SECTIONS_KEY","Symbol","for","globalThis","sections","Map","listDeclaredSiteBundleSections","registerPluginSiteBundleSection","key","section","options","name","trim","pluginId","Error","declared","find","one","package","dependencies","remapIds","kind","set","resolveSiteBundleSections","missing","filter","has","length","absent","map","join","get","resetSiteBundleSectionsForTests","clear"],"mappings":";IA6LEA;AA7LF;;;;;;;;;;;;;;;CAeC,GAED,SAASC,sBAAsB,QAAQ,qCAAiC;AACxE,SAASC,oCAAoC,QAAQ,qCAAiC;AACtF,SAASC,2BAA2B,QAAQ,kCAA8B;AA6J1E;;;;;CAKC,GACD,MAAMC,eAAeC,OAAOC,GAAG,CAAC;AAEhC,MAAMN,cAAcO;AAIpB,MAAMC,YACJR,4BAAAA,WAAW,CAACI,aAAa,YAAzBJ,4BAA8BA,WAAW,CAACI,aAAa,GAAG,IAAIK;AAEhE,iFAAiF,GACjF,OAAO,SAASC;IACd,OAAOR;AACT;AAEA;;;;;;;CAOC,GACD,OAAO,SAASS,gCACdC,GAAW,EACXC,OAAgC,EAChCC,OAA+B;QAGbb,MAAAA;QAqBTY,kBACAA;IAvBT,MAAME,OAAOH,IAAII,IAAI;IACrB,MAAMC,WAAW,EAAChB,QAAAA,0BAAAA,oCAAAA,0BAA4Ba,2BAAAA,QAASG,QAAQ,YAA7ChB,OAAiD,IAAIe,IAAI;IAC3E,IAAI,CAACC,UAAU;QACb,MAAM,IAAIC,MACR,CAAC,qBAAqB,EAAEH,KAAK,8CAA8C,CAAC,GAC1E;IAEN;IACA,MAAMI,WAAWT,iCAAiCU,IAAI,CAAC,CAACC,MAAQA,IAAIT,GAAG,KAAKG;IAC5E,IAAI,CAACI,UAAU;QACb,MAAM,IAAID,MACR,CAAC,qBAAqB,EAAEH,KAAK,yCAAyC,CAAC,GACrE;IAEN;IACA,IAAII,SAASF,QAAQ,KAAKA,UAAU;QAClC,MAAM,IAAIC,MACR,CAAC,qBAAqB,EAAEH,KAAK,kBAAkB,EAAEI,SAASF,QAAQ,CAAC,GAAG,CAAC,GACrE,CAAC,SAAS,EAAEA,SAAS,CAAC,CAAC;IAE7B;IACA,IACE,SAAOJ,mBAAAA,QAAQS,OAAO,qBAAfT,iBAAiBU,YAAY,MAAK,cACzC,SAAOV,oBAAAA,QAAQS,OAAO,qBAAfT,kBAAiBW,QAAQ,MAAK,YACrC;QACA,MAAM,IAAIN,MACR,CAAC,qBAAqB,EAAEH,KAAK,yBAAyB,EAAEI,SAASG,OAAO,CAACG,IAAI,CAAC,EAAE,CAAC,GAC/E,2EACA;IAEN;IACAjB,SAASkB,GAAG,CAACX,MAAM;QAAEE;QAAUJ;IAAQ;AACzC;AAOA;;;;;CAKC,GACD,OAAO,eAAec;IACpB,MAAMC,UAAU,IACdlB,iCAAiCmB,MAAM,CAAC,CAACV,WAAa,CAACX,SAASsB,GAAG,CAACX,SAASP,GAAG;IAClF,IAAIgB,UAAUG,MAAM,EAAE,MAAM5B;IAC5B,MAAM6B,SAASJ;IACf,IAAII,OAAOD,MAAM,EAAE;QACjB,MAAM,IAAIb,MACR,CAAC,iCAAiC,EAAEc,OAAOC,GAAG,CAAC,CAACZ,MAAQ,CAAC,CAAC,EAAEA,IAAIT,GAAG,CAAC,GAAG,EAAES,IAAIJ,QAAQ,CAAC,CAAC,CAAC,EAAEiB,IAAI,CAAC,MAAM,CAAC,CAAC,GACrG;IAEN;IACA,OAAOxB,iCAAiCuB,GAAG,CAAC,CAACd,WAAc,aACtDA;YACHN,SAAS,AAACL,SAAS2B,GAAG,CAAChB,SAASP,GAAG,EAAiBC,OAAO;;AAE/D;AAEA,sDAAsD,GACtD,OAAO,SAASuB;IACd5B,SAAS6B,KAAK;AAChB"}