@aglyn/aglyn 1.0.0-beta.217 → 1.0.0-beta.219

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 (348) hide show
  1. package/package.json +14 -14
  2. package/src/lib/app-utils/actions.d.ts +25 -374
  3. package/src/lib/app-utils/actions.js +101 -561
  4. package/src/lib/app-utils/actions.js.map +1 -1
  5. package/src/lib/app-utils/activity-presenter.d.ts +3 -3
  6. package/src/lib/app-utils/activity-presenter.js +36 -52
  7. package/src/lib/app-utils/activity-presenter.js.map +1 -1
  8. package/src/lib/app-utils/admin-audit-index.js +2 -2
  9. package/src/lib/app-utils/admin-audit-index.js.map +1 -1
  10. package/src/lib/app-utils/analytics-events.d.ts +11 -2
  11. package/src/lib/app-utils/analytics-events.js +11 -2
  12. package/src/lib/app-utils/analytics-events.js.map +1 -1
  13. package/src/lib/app-utils/analytics-provider.d.ts +182 -0
  14. package/src/lib/app-utils/analytics-provider.js +133 -0
  15. package/src/lib/app-utils/analytics-provider.js.map +1 -0
  16. package/src/lib/app-utils/api-plugins.d.ts +46 -0
  17. package/src/lib/app-utils/api-plugins.js +19 -0
  18. package/src/lib/app-utils/api-plugins.js.map +1 -1
  19. package/src/lib/app-utils/{marketplace-overrides.d.ts → artifact-overrides.d.ts} +9 -6
  20. package/src/lib/app-utils/{marketplace-overrides.js → artifact-overrides.js} +10 -7
  21. package/src/lib/app-utils/artifact-overrides.js.map +1 -0
  22. package/src/lib/app-utils/{marketplace-provenance.d.ts → artifact-provenance.d.ts} +12 -10
  23. package/src/lib/app-utils/{marketplace-provenance.js → artifact-provenance.js} +9 -7
  24. package/src/lib/app-utils/artifact-provenance.js.map +1 -0
  25. package/src/lib/app-utils/assist-credits.js +2 -1
  26. package/src/lib/app-utils/assist-credits.js.map +1 -1
  27. package/src/lib/app-utils/binding-token-catalog.d.ts +5 -13
  28. package/src/lib/app-utils/binding-token-catalog.js +17 -41
  29. package/src/lib/app-utils/binding-token-catalog.js.map +1 -1
  30. package/src/lib/app-utils/binding-tokens.js +1 -1
  31. package/src/lib/app-utils/binding-tokens.js.map +1 -1
  32. package/src/lib/app-utils/brand-support-line.d.ts +36 -0
  33. package/src/lib/app-utils/brand-support-line.js +38 -0
  34. package/src/lib/app-utils/brand-support-line.js.map +1 -0
  35. package/src/lib/app-utils/campaign-touch.d.ts +5 -5
  36. package/src/lib/app-utils/campaign-touch.js +8 -8
  37. package/src/lib/app-utils/campaign-touch.js.map +1 -1
  38. package/src/lib/app-utils/collection-entries.d.ts +2 -2
  39. package/src/lib/app-utils/collection-entries.js.map +1 -1
  40. package/src/lib/app-utils/consent-group-change.d.ts +4 -0
  41. package/src/lib/app-utils/consent-group-change.js.map +1 -1
  42. package/src/lib/app-utils/console-record-links.d.ts +10 -89
  43. package/src/lib/app-utils/console-record-links.js +3 -107
  44. package/src/lib/app-utils/console-record-links.js.map +1 -1
  45. package/src/lib/app-utils/console-routes.d.ts +16 -7
  46. package/src/lib/app-utils/console-routes.js +9 -3
  47. package/src/lib/app-utils/console-routes.js.map +1 -1
  48. package/src/lib/app-utils/contacts.d.ts +23 -1
  49. package/src/lib/app-utils/contacts.js +21 -5
  50. package/src/lib/app-utils/contacts.js.map +1 -1
  51. package/src/lib/app-utils/cross-pool-desync.d.ts +39 -0
  52. package/src/lib/app-utils/cross-pool-desync.js +47 -0
  53. package/src/lib/app-utils/cross-pool-desync.js.map +1 -0
  54. package/src/lib/app-utils/csv-upload.d.ts +29 -0
  55. package/src/lib/app-utils/csv-upload.js +29 -0
  56. package/src/lib/app-utils/csv-upload.js.map +1 -0
  57. package/src/lib/app-utils/dataset-models.d.ts +5 -5
  58. package/src/lib/app-utils/dataset-models.js +19 -2
  59. package/src/lib/app-utils/dataset-models.js.map +1 -1
  60. package/src/lib/app-utils/datasets.d.ts +65 -11
  61. package/src/lib/app-utils/datasets.js +74 -11
  62. package/src/lib/app-utils/datasets.js.map +1 -1
  63. package/src/lib/app-utils/docs-deflection.d.ts +6 -6
  64. package/src/lib/app-utils/docs-deflection.js.map +1 -1
  65. package/src/lib/app-utils/docs-help.generated.d.ts +12 -6
  66. package/src/lib/app-utils/docs-help.generated.js +21 -6
  67. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  68. package/src/lib/app-utils/docs-index.generated.d.ts +2 -2
  69. package/src/lib/app-utils/docs-index.generated.js +514 -318
  70. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  71. package/src/lib/app-utils/docs-retrieval.d.ts +3 -3
  72. package/src/lib/app-utils/docs-retrieval.js +14 -10
  73. package/src/lib/app-utils/docs-retrieval.js.map +1 -1
  74. package/src/lib/app-utils/duplicate-resource.d.ts +4 -1
  75. package/src/lib/app-utils/duplicate-resource.js +7 -4
  76. package/src/lib/app-utils/duplicate-resource.js.map +1 -1
  77. package/src/lib/app-utils/dynamic-list-rule.d.ts +1 -1
  78. package/src/lib/app-utils/dynamic-list-rule.js.map +1 -1
  79. package/src/lib/app-utils/element-ui.d.ts +1 -1
  80. package/src/lib/app-utils/element-ui.js +1 -1
  81. package/src/lib/app-utils/element-ui.js.map +1 -1
  82. package/src/lib/app-utils/email-starter-policy.js +3 -3
  83. package/src/lib/app-utils/email-starter-policy.js.map +1 -1
  84. package/src/lib/app-utils/entity-picker-context.d.ts +10 -5
  85. package/src/lib/app-utils/entity-picker-context.js.map +1 -1
  86. package/src/lib/app-utils/error-beacon.d.ts +75 -6
  87. package/src/lib/app-utils/error-beacon.js +160 -21
  88. package/src/lib/app-utils/error-beacon.js.map +1 -1
  89. package/src/lib/app-utils/expand-repeatables.d.ts +10 -10
  90. package/src/lib/app-utils/expand-repeatables.js +10 -10
  91. package/src/lib/app-utils/expand-repeatables.js.map +1 -1
  92. package/src/lib/app-utils/host-events.d.ts +82 -0
  93. package/src/lib/app-utils/host-events.js +62 -0
  94. package/src/lib/app-utils/host-events.js.map +1 -0
  95. package/src/lib/app-utils/lockdown.d.ts +33 -1
  96. package/src/lib/app-utils/lockdown.js +45 -4
  97. package/src/lib/app-utils/lockdown.js.map +1 -1
  98. package/src/lib/app-utils/margin-utilization.d.ts +4 -69
  99. package/src/lib/app-utils/margin-utilization.js +82 -69
  100. package/src/lib/app-utils/margin-utilization.js.map +1 -1
  101. package/src/lib/app-utils/node-definition-sanitizer.d.ts +16 -16
  102. package/src/lib/app-utils/node-definition-sanitizer.js +24 -20
  103. package/src/lib/app-utils/node-definition-sanitizer.js.map +1 -1
  104. package/src/lib/app-utils/node-interactions.d.ts +7 -7
  105. package/src/lib/app-utils/node-interactions.js.map +1 -1
  106. package/src/lib/app-utils/notifications.d.ts +78 -47
  107. package/src/lib/app-utils/notifications.js +63 -44
  108. package/src/lib/app-utils/notifications.js.map +1 -1
  109. package/src/lib/app-utils/payments-configured.d.ts +8 -21
  110. package/src/lib/app-utils/payments-configured.js +8 -23
  111. package/src/lib/app-utils/payments-configured.js.map +1 -1
  112. package/src/lib/app-utils/plan-entitlements.d.ts +43 -128
  113. package/src/lib/app-utils/plan-entitlements.js +145 -304
  114. package/src/lib/app-utils/plan-entitlements.js.map +1 -1
  115. package/src/lib/app-utils/platform-brand.d.ts +10 -0
  116. package/src/lib/app-utils/platform-brand.js +14 -3
  117. package/src/lib/app-utils/platform-brand.js.map +1 -1
  118. package/src/lib/app-utils/plugin-api-rate-limit.d.ts +7 -1
  119. package/src/lib/app-utils/plugin-api-rate-limit.js +11 -3
  120. package/src/lib/app-utils/plugin-api-rate-limit.js.map +1 -1
  121. package/src/lib/app-utils/plugin-bundle-checks.js +2 -2
  122. package/src/lib/app-utils/plugin-bundle-checks.js.map +1 -1
  123. package/src/lib/app-utils/plugin-host-events.generated.d.ts +12 -0
  124. package/src/lib/app-utils/plugin-host-events.generated.js +169 -0
  125. package/src/lib/app-utils/plugin-host-events.generated.js.map +1 -0
  126. package/src/lib/app-utils/plugin-release-flags.generated.d.ts +12 -0
  127. package/src/lib/app-utils/plugin-release-flags.generated.js +106 -0
  128. package/src/lib/app-utils/plugin-release-flags.generated.js.map +1 -0
  129. package/src/lib/app-utils/realm-host-surface.generated.js +2 -0
  130. package/src/lib/app-utils/realm-host-surface.generated.js.map +1 -1
  131. package/src/lib/app-utils/release-flags.d.ts +14 -5
  132. package/src/lib/app-utils/release-flags.js +21 -125
  133. package/src/lib/app-utils/release-flags.js.map +1 -1
  134. package/src/lib/app-utils/repeat-sources.d.ts +9 -0
  135. package/src/lib/app-utils/repeat-sources.js.map +1 -1
  136. package/src/lib/app-utils/retained-refusals.d.ts +73 -0
  137. package/src/lib/app-utils/retained-refusals.js +186 -0
  138. package/src/lib/app-utils/retained-refusals.js.map +1 -0
  139. package/src/lib/app-utils/scope-tokens.d.ts +27 -0
  140. package/src/lib/app-utils/scope-tokens.js +61 -0
  141. package/src/lib/app-utils/scope-tokens.js.map +1 -1
  142. package/src/lib/app-utils/screen-link-context.js +3 -3
  143. package/src/lib/app-utils/screen-link-context.js.map +1 -1
  144. package/src/lib/app-utils/screen-route.d.ts +15 -0
  145. package/src/lib/app-utils/screen-route.js +21 -1
  146. package/src/lib/app-utils/screen-route.js.map +1 -1
  147. package/src/lib/app-utils/seo-audit.d.ts +132 -0
  148. package/src/lib/app-utils/seo-audit.js +246 -0
  149. package/src/lib/app-utils/seo-audit.js.map +1 -0
  150. package/src/lib/app-utils/seo-keywords.d.ts +48 -0
  151. package/src/lib/app-utils/seo-keywords.js +60 -0
  152. package/src/lib/app-utils/seo-keywords.js.map +1 -0
  153. package/src/lib/app-utils/seo-page-facts.d.ts +66 -0
  154. package/src/lib/app-utils/seo-page-facts.js +155 -0
  155. package/src/lib/app-utils/seo-page-facts.js.map +1 -0
  156. package/src/lib/app-utils/seo-site-scan.d.ts +126 -0
  157. package/src/lib/app-utils/seo-site-scan.js +162 -0
  158. package/src/lib/app-utils/seo-site-scan.js.map +1 -0
  159. package/src/lib/app-utils/seo-title-variables.js +1 -1
  160. package/src/lib/app-utils/seo-title-variables.js.map +1 -1
  161. package/src/lib/app-utils/server.d.ts +6 -9
  162. package/src/lib/app-utils/server.js +17 -27
  163. package/src/lib/app-utils/server.js.map +1 -1
  164. package/src/lib/app-utils/site-interactions.d.ts +501 -0
  165. package/src/lib/app-utils/site-interactions.js +554 -0
  166. package/src/lib/app-utils/site-interactions.js.map +1 -0
  167. package/src/lib/app-utils/{marketplace-theme.d.ts → site-theme.d.ts} +13 -2
  168. package/src/lib/app-utils/{marketplace-theme.js → site-theme.js} +5 -2
  169. package/src/lib/app-utils/site-theme.js.map +1 -0
  170. package/src/lib/app-utils/sitemap.d.ts +4 -3
  171. package/src/lib/app-utils/sitemap.js +9 -7
  172. package/src/lib/app-utils/sitemap.js.map +1 -1
  173. package/src/lib/app-utils/stale-build-error.d.ts +51 -0
  174. package/src/lib/app-utils/stale-build-error.js +100 -1
  175. package/src/lib/app-utils/stale-build-error.js.map +1 -1
  176. package/src/lib/app-utils/starter-templates.d.ts +20 -0
  177. package/src/lib/app-utils/starter-templates.js +49 -0
  178. package/src/lib/app-utils/starter-templates.js.map +1 -1
  179. package/src/lib/app-utils/tax-jurisdiction-figures.d.ts +118 -0
  180. package/src/lib/app-utils/tax-jurisdiction-figures.js +123 -0
  181. package/src/lib/app-utils/tax-jurisdiction-figures.js.map +1 -0
  182. package/src/lib/app-utils/theme-library.d.ts +10 -3
  183. package/src/lib/app-utils/theme-library.js +13 -4
  184. package/src/lib/app-utils/theme-library.js.map +1 -1
  185. package/src/lib/app-utils/{campaign-attribution.d.ts → utm-attribution.d.ts} +11 -6
  186. package/src/lib/app-utils/{campaign-attribution.js → utm-attribution.js} +13 -8
  187. package/src/lib/app-utils/utm-attribution.js.map +1 -0
  188. package/src/lib/app-utils/{campaign-forwarding.d.ts → utm-forwarding.d.ts} +14 -14
  189. package/src/lib/app-utils/{campaign-forwarding.js → utm-forwarding.js} +26 -26
  190. package/src/lib/app-utils/utm-forwarding.js.map +1 -0
  191. package/src/lib/app-utils/video-object.js +6 -5
  192. package/src/lib/app-utils/video-object.js.map +1 -1
  193. package/src/lib/app-utils/visitor-consent.d.ts +41 -164
  194. package/src/lib/app-utils/visitor-consent.js +66 -277
  195. package/src/lib/app-utils/visitor-consent.js.map +1 -1
  196. package/src/lib/app-utils/web-vitals-rum.d.ts +7 -7
  197. package/src/lib/app-utils/web-vitals-rum.js +31 -63
  198. package/src/lib/app-utils/web-vitals-rum.js.map +1 -1
  199. package/src/lib/app-utils/where-used.d.ts +11 -6
  200. package/src/lib/app-utils/where-used.js +3 -8
  201. package/src/lib/app-utils/where-used.js.map +1 -1
  202. package/src/lib/canvas-manager/canvas-manager.js +3 -3
  203. package/src/lib/canvas-manager/canvas-manager.js.map +1 -1
  204. package/src/lib/foundation/definitions/components.types.d.ts +1 -1
  205. package/src/lib/foundation/definitions/components.types.js +1 -1
  206. package/src/lib/foundation/definitions/components.types.js.map +1 -1
  207. package/src/lib/foundation/definitions/org-billing.types.d.ts +18 -135
  208. package/src/lib/foundation/definitions/org-billing.types.js.map +1 -1
  209. package/src/lib/foundation/definitions/organization.types.d.ts +6 -3
  210. package/src/lib/foundation/definitions/organization.types.js.map +1 -1
  211. package/src/lib/foundation/definitions/platform.types.d.ts +27 -5
  212. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  213. package/src/lib/foundation/definitions/write-deny-coverage.util.d.ts +16 -0
  214. package/src/lib/foundation/definitions/write-deny-coverage.util.js +20 -0
  215. package/src/lib/foundation/definitions/write-deny-coverage.util.js.map +1 -1
  216. package/src/lib/foundation/index.d.ts +1 -1
  217. package/src/lib/foundation/index.js.map +1 -1
  218. package/src/lib/plugin-manager/besigner-documents.d.ts +96 -0
  219. package/src/lib/plugin-manager/besigner-documents.js +61 -0
  220. package/src/lib/plugin-manager/besigner-documents.js.map +1 -0
  221. package/src/lib/plugin-manager/computed-variables.d.ts +71 -0
  222. package/src/lib/plugin-manager/computed-variables.js +74 -0
  223. package/src/lib/plugin-manager/computed-variables.js.map +1 -0
  224. package/src/lib/plugin-manager/custom-fields.d.ts +29 -2
  225. package/src/lib/plugin-manager/custom-fields.js +47 -1
  226. package/src/lib/plugin-manager/custom-fields.js.map +1 -1
  227. package/src/lib/plugin-manager/feature-plugins.d.ts +292 -20
  228. package/src/lib/plugin-manager/feature-plugins.js +105 -17
  229. package/src/lib/plugin-manager/feature-plugins.js.map +1 -1
  230. package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +123 -0
  231. package/src/lib/plugin-manager/first-party-plugins.generated.js +785 -14
  232. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  233. package/src/lib/plugin-manager/plugin-activity-actions.d.ts +12 -0
  234. package/src/lib/plugin-manager/plugin-activity-actions.js +25 -0
  235. package/src/lib/plugin-manager/plugin-activity-actions.js.map +1 -1
  236. package/src/lib/plugin-manager/plugin-contributions.d.ts +1 -1
  237. package/src/lib/plugin-manager/plugin-contributions.js.map +1 -1
  238. package/src/lib/plugin-manager/plugin-declarations-repair.d.ts +36 -0
  239. package/src/lib/plugin-manager/plugin-declarations-repair.js +64 -0
  240. package/src/lib/plugin-manager/plugin-declarations-repair.js.map +1 -0
  241. package/src/lib/plugin-manager/plugin-dependents.d.ts +101 -0
  242. package/src/lib/plugin-manager/plugin-dependents.js +75 -0
  243. package/src/lib/plugin-manager/plugin-dependents.js.map +1 -0
  244. package/src/lib/plugin-manager/plugin-distribution.d.ts +55 -0
  245. package/src/lib/plugin-manager/plugin-distribution.js +45 -0
  246. package/src/lib/plugin-manager/plugin-distribution.js.map +1 -0
  247. package/src/lib/plugin-manager/plugin-host-collections.d.ts +17 -0
  248. package/src/lib/plugin-manager/plugin-host-collections.js +6 -0
  249. package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
  250. package/src/lib/plugin-manager/plugin-host-resources.d.ts +133 -0
  251. package/src/lib/plugin-manager/plugin-host-resources.js +52 -0
  252. package/src/lib/plugin-manager/plugin-host-resources.js.map +1 -0
  253. package/src/lib/plugin-manager/plugin-org-erasure.d.ts +28 -2
  254. package/src/lib/plugin-manager/plugin-org-erasure.js +61 -2
  255. package/src/lib/plugin-manager/plugin-org-erasure.js.map +1 -1
  256. package/src/lib/plugin-manager/plugin-plan-entitlements.d.ts +123 -0
  257. package/src/lib/plugin-manager/plugin-plan-entitlements.js +101 -0
  258. package/src/lib/plugin-manager/plugin-plan-entitlements.js.map +1 -0
  259. package/src/lib/plugin-manager/plugin-record-index.d.ts +95 -0
  260. package/src/lib/plugin-manager/plugin-record-index.js +51 -0
  261. package/src/lib/plugin-manager/plugin-record-index.js.map +1 -0
  262. package/src/lib/plugin-manager/plugin-record-written.d.ts +58 -0
  263. package/src/lib/plugin-manager/plugin-record-written.js +42 -0
  264. package/src/lib/plugin-manager/plugin-record-written.js.map +1 -0
  265. package/src/lib/plugin-manager/plugin-revenue-sources.d.ts +213 -0
  266. package/src/lib/plugin-manager/plugin-revenue-sources.js +194 -0
  267. package/src/lib/plugin-manager/plugin-revenue-sources.js.map +1 -0
  268. package/src/lib/plugin-manager/plugin-site-bundle.d.ts +145 -0
  269. package/src/lib/plugin-manager/plugin-site-bundle.js +78 -0
  270. package/src/lib/plugin-manager/plugin-site-bundle.js.map +1 -0
  271. package/src/lib/plugin-manager/plugin-site-cache.d.ts +12 -0
  272. package/src/lib/plugin-manager/plugin-site-cache.js.map +1 -1
  273. package/src/lib/plugin-manager/plugin-site-export.d.ts +81 -0
  274. package/src/lib/plugin-manager/plugin-site-export.js +40 -0
  275. package/src/lib/plugin-manager/plugin-site-export.js.map +1 -0
  276. package/src/lib/plugin-manager/plugin-sitemap-sections.d.ts +88 -0
  277. package/src/lib/plugin-manager/plugin-sitemap-sections.js +29 -0
  278. package/src/lib/plugin-manager/plugin-sitemap-sections.js.map +1 -0
  279. package/src/lib/plugin-manager/plugin-tax-profile.d.ts +20 -4
  280. package/src/lib/plugin-manager/plugin-tax-profile.js.map +1 -1
  281. package/src/lib/plugin-manager/plugin-tax-return-sources.d.ts +255 -0
  282. package/src/lib/plugin-manager/plugin-tax-return-sources.js +191 -0
  283. package/src/lib/plugin-manager/plugin-tax-return-sources.js.map +1 -0
  284. package/src/lib/plugin-manager/plugin-template-sources.d.ts +75 -0
  285. package/src/lib/plugin-manager/plugin-template-sources.js +37 -0
  286. package/src/lib/plugin-manager/plugin-template-sources.js.map +1 -0
  287. package/src/lib/plugin-manager/plugin-usage-axes.d.ts +107 -0
  288. package/src/lib/plugin-manager/plugin-usage-axes.js +107 -0
  289. package/src/lib/plugin-manager/plugin-usage-axes.js.map +1 -0
  290. package/src/lib/plugin-manager/plugin-zones.d.ts +4 -5
  291. package/src/lib/plugin-manager/plugin-zones.js.map +1 -1
  292. package/src/lib/plugin-manager/realm-host-aglyn.generated.js +2 -0
  293. package/src/lib/plugin-manager/realm-host-aglyn.generated.js.map +1 -1
  294. package/src/lib/plugin-manager/record-captured-contact.d.ts +1 -14
  295. package/src/lib/plugin-manager/record-captured-contact.js +10 -32
  296. package/src/lib/plugin-manager/record-captured-contact.js.map +1 -1
  297. package/src/lib/plugin-manager/repeat-rows.d.ts +124 -0
  298. package/src/lib/plugin-manager/repeat-rows.js +108 -0
  299. package/src/lib/plugin-manager/repeat-rows.js.map +1 -0
  300. package/src/lib/plugin-manager/submission-record-target.d.ts +144 -0
  301. package/src/lib/plugin-manager/submission-record-target.js +125 -0
  302. package/src/lib/plugin-manager/submission-record-target.js.map +1 -0
  303. package/src/lib/plugin-manager/video-embed-provider.d.ts +108 -0
  304. package/src/lib/plugin-manager/video-embed-provider.js +165 -0
  305. package/src/lib/plugin-manager/video-embed-provider.js.map +1 -0
  306. package/src/lib/types/nodes.d.ts +2 -2
  307. package/src/lib/types/nodes.js.map +1 -1
  308. package/src/lib/app-utils/campaign-attribution.js.map +0 -1
  309. package/src/lib/app-utils/campaign-forwarding.js.map +0 -1
  310. package/src/lib/app-utils/contact-holder.d.ts +0 -68
  311. package/src/lib/app-utils/contact-holder.js +0 -129
  312. package/src/lib/app-utils/contact-holder.js.map +0 -1
  313. package/src/lib/app-utils/contact-merge.d.ts +0 -136
  314. package/src/lib/app-utils/contact-merge.js +0 -412
  315. package/src/lib/app-utils/contact-merge.js.map +0 -1
  316. package/src/lib/app-utils/crm-csv.d.ts +0 -239
  317. package/src/lib/app-utils/crm-csv.js +0 -467
  318. package/src/lib/app-utils/crm-csv.js.map +0 -1
  319. package/src/lib/app-utils/crm-digest.d.ts +0 -185
  320. package/src/lib/app-utils/crm-digest.js +0 -272
  321. package/src/lib/app-utils/crm-digest.js.map +0 -1
  322. package/src/lib/app-utils/form-dataset-binding.d.ts +0 -50
  323. package/src/lib/app-utils/form-dataset-binding.js +0 -74
  324. package/src/lib/app-utils/form-dataset-binding.js.map +0 -1
  325. package/src/lib/app-utils/list-import.d.ts +0 -158
  326. package/src/lib/app-utils/list-import.js +0 -332
  327. package/src/lib/app-utils/list-import.js.map +0 -1
  328. package/src/lib/app-utils/marketplace-listing-visibility.d.ts +0 -122
  329. package/src/lib/app-utils/marketplace-listing-visibility.js +0 -147
  330. package/src/lib/app-utils/marketplace-listing-visibility.js.map +0 -1
  331. package/src/lib/app-utils/marketplace-overrides.js.map +0 -1
  332. package/src/lib/app-utils/marketplace-provenance.js.map +0 -1
  333. package/src/lib/app-utils/marketplace-theme.js.map +0 -1
  334. package/src/lib/app-utils/marketplace-update-state.d.ts +0 -119
  335. package/src/lib/app-utils/marketplace-update-state.js +0 -132
  336. package/src/lib/app-utils/marketplace-update-state.js.map +0 -1
  337. package/src/lib/app-utils/publisher-agreement.d.ts +0 -289
  338. package/src/lib/app-utils/publisher-agreement.js +0 -321
  339. package/src/lib/app-utils/publisher-agreement.js.map +0 -1
  340. package/src/lib/app-utils/wistia-embed.d.ts +0 -104
  341. package/src/lib/app-utils/wistia-embed.js +0 -138
  342. package/src/lib/app-utils/wistia-embed.js.map +0 -1
  343. package/src/lib/app-utils/workflows.d.ts +0 -136
  344. package/src/lib/app-utils/workflows.js +0 -346
  345. package/src/lib/app-utils/workflows.js.map +0 -1
  346. package/src/lib/plugin-manager/commerce-zone-props.d.ts +0 -172
  347. package/src/lib/plugin-manager/commerce-zone-props.js +0 -31
  348. package/src/lib/plugin-manager/commerce-zone-props.js.map +0 -1
@@ -0,0 +1,182 @@
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 { VisitorConsentHost } from './visitor-consent';
18
+ /**
19
+ * Third-party analytics tags, behind one contract (AGL-3080).
20
+ *
21
+ * A site can send its visitors' measurement to an analytics vendor it chose:
22
+ * a measurement id or a tag container, saved in the site's analytics
23
+ * settings. The platform owns everything around that tag — whether this
24
+ * visitor may be measured at all (`visitor-consent.ts`), the consent banner,
25
+ * the first-party pageview beacon that meters the site (`analytics-beacon.ts`,
26
+ * which is not a tag and never goes through here) — and names no vendor. A
27
+ * plugin's ADAPTER is what knows a vendor: what its tag looks like on the
28
+ * page, how a resident tag is told the visitor's answer changed, and how an
29
+ * event reaches it.
30
+ *
31
+ * ## Declared, then loaded where it is used
32
+ *
33
+ * Two questions, answered by two different mechanisms, because their readers
34
+ * cannot wait for the same thing:
35
+ *
36
+ * - "Does this site run a tag, so is there anything to ask its visitors?" is
37
+ * asked synchronously, during render, by the consent gate on the published
38
+ * page and by the console's banner card and preview. A registry one of
39
+ * those had not filled yet would answer "no tag" — no banner, and nothing to
40
+ * gate — so the answer is COMPILED: the plugin declares which of the site's
41
+ * analytics settings it mounts a tag for (`analyticsProvider.settings` in
42
+ * `plugins.config.json`), and `hostConfiguresAnalyticsTag` in
43
+ * `visitor-consent.ts` reads that list. With no provider declared, no setting configures a tag, and a site
44
+ * with nothing that could mount asks its visitors nothing.
45
+ * - Everything else — mounting the tag, telling it the answer changed,
46
+ * handing it an event — happens after the adapter has loaded. The adapter
47
+ * is a chunk of its own, reached through each app's generated
48
+ * `plugins.analytics.generated.ts` and fetched only by a document that uses
49
+ * it: a published page whose site configures a tag, and the console, whose
50
+ * own measurement tag the consent controls answer to. A page whose site
51
+ * configures none never fetches it.
52
+ *
53
+ * ## What an unloaded adapter means
54
+ *
55
+ * A tag is mounted only by its adapter, so until the adapter has loaded there
56
+ * is no tag of its to silence or to send to, and nothing is lost by the wait:
57
+ * events find no resident tag and drop, exactly as they do for a visitor who
58
+ * has not granted.
59
+ *
60
+ * A tag this document did not mount — the console's, which its analytics SDK
61
+ * injects — can be resident before the adapter arrives. So the last answer
62
+ * {@link applyAnalyticsConsent} was given is kept, and an adapter that
63
+ * registers later is handed it at once: a withdrawal made in that window
64
+ * reaches the tag the moment something can speak to it.
65
+ */
66
+ /**
67
+ * What a provider declares about itself, compiled from `plugins.config.json`.
68
+ * `settings` are keys of the site's `analytics` settings; each is validated
69
+ * by the pattern `ANALYTICS_SETTING_PATTERNS` in `visitor-consent.ts` holds
70
+ * for it.
71
+ */
72
+ export interface AnalyticsProviderDeclaration {
73
+ /** The plugin that provides the adapter. */
74
+ pluginId: string;
75
+ /** The analytics settings this provider mounts a tag for. */
76
+ settings: readonly string[];
77
+ }
78
+ /** The visitor's answer, in the platform's two categories. */
79
+ export interface AnalyticsGrants {
80
+ analytics: boolean;
81
+ advertising: boolean;
82
+ }
83
+ /**
84
+ * One tag on a published page: an inline boot that runs first, then the
85
+ * vendor's library. The page renders the pair in order, under the consent
86
+ * gate and with the request's nonce, as `${id}-init` and `${id}-src`.
87
+ *
88
+ * Both strings land in the document. `boot` is inline script, so an adapter
89
+ * builds it from constants and format-checked ids only.
90
+ */
91
+ export interface AnalyticsTagMount {
92
+ /** Element id prefix. Stable: specs and the CSP report read it. */
93
+ id: string;
94
+ /** Inline script that runs before the library. */
95
+ boot: string;
96
+ /** The library URL. */
97
+ src: string;
98
+ /**
99
+ * A marker for the library this pair loads, for an advertising vendor that
100
+ * rides the same library instead of fetching it again (`sharesLibrary` in
101
+ * `advertising-tags.ts`).
102
+ */
103
+ library?: string;
104
+ }
105
+ /** What the page tells an adapter about the pageview it mounts tags for. */
106
+ export interface AnalyticsMountOptions {
107
+ /**
108
+ * Whether the site runs the platform's consent machinery. When it does, the
109
+ * tag is told the visitor's answer before it measures anything; when the
110
+ * host runs a consent solution of its own, that solution tells it.
111
+ */
112
+ consentRequired: boolean;
113
+ /** Whether this visitor granted advertising storage on this site. */
114
+ advertising: boolean;
115
+ }
116
+ export interface AnalyticsEventOptions {
117
+ /**
118
+ * Address only the measurement destinations of the resident tag, not any
119
+ * advertising destination sharing its library. Set for events that no
120
+ * advertising report reads, such as Core Web Vitals.
121
+ */
122
+ measurementOnly?: boolean;
123
+ }
124
+ /** A vendor's adapter. */
125
+ export interface AnalyticsProvider {
126
+ /**
127
+ * The tags a granted pageview mounts for `host`, in order; empty when the
128
+ * host configures none of this provider's settings, or configures them
129
+ * malformed. Called only once the page's consent gate has said yes.
130
+ */
131
+ mounts(host: VisitorConsentHost | null | undefined, options: AnalyticsMountOptions): readonly AnalyticsTagMount[];
132
+ /**
133
+ * Make every resident tag of this vendor agree with `grants`, and answer
134
+ * the ids it acted on. A page cannot unload a script, so a withdrawal in
135
+ * the middle of a pageview has to be told to the tag that is already there.
136
+ */
137
+ applyConsent(grants: AnalyticsGrants): readonly string[];
138
+ /** Whether a tag of this vendor is resident and can take an event now. */
139
+ resident(): boolean;
140
+ /** Hand one event to the resident tag. Never throws. */
141
+ sendEvent(name: string, params: Record<string, unknown>, options?: AnalyticsEventOptions): void;
142
+ }
143
+ /** An adapter module, as an app's generated manifest loads it. */
144
+ export interface AnalyticsProviderModule {
145
+ analyticsProvider: AnalyticsProvider;
146
+ }
147
+ /** One row of an app's generated `plugins.analytics.generated.ts`. */
148
+ export interface AnalyticsProviderLoader {
149
+ pluginId: string;
150
+ load: () => Promise<AnalyticsProviderModule>;
151
+ }
152
+ /**
153
+ * Registers `pluginId`'s adapter. The same plugin registering again replaces
154
+ * its adapter. If a consent answer was already given in this document, the
155
+ * adapter is told it before anything else can reach it.
156
+ */
157
+ export declare function registerAnalyticsProvider(pluginId: string, provider: AnalyticsProvider): void;
158
+ /** The registered adapters, in registration order. A stable array. */
159
+ export declare function analyticsProviders(): readonly AnalyticsProvider[];
160
+ /** Hears every registration. Answers the unsubscribe. */
161
+ export declare function subscribeAnalyticsProviders(listener: () => void): () => void;
162
+ /**
163
+ * Tell every resident tag the visitor's answer, and answer the ids acted on.
164
+ * Advertising is clamped to analytics: a refusal withdraws both.
165
+ */
166
+ export declare function applyAnalyticsConsent(grants: AnalyticsGrants): string[];
167
+ /** Whether any registered adapter has a tag resident that can take an event. */
168
+ export declare function analyticsTagResident(): boolean;
169
+ /**
170
+ * Hand one event to every resident tag. Answers whether any took it, so a
171
+ * caller that may hold an event while a tag is still loading knows to.
172
+ */
173
+ export declare function sendAnalyticsProviderEvent(name: string, params: Record<string, unknown>, options?: AnalyticsEventOptions): boolean;
174
+ /**
175
+ * Fetch and register each adapter once per document. Idempotent and safe to
176
+ * call during render: it starts the fetch, never waits for it, and a loader
177
+ * that fails leaves that provider unregistered — the vendor's tag then never
178
+ * mounts, which is the outcome a failed vendor library has anyway.
179
+ */
180
+ export declare function loadAnalyticsProviders(loaders: readonly AnalyticsProviderLoader[]): Promise<void>;
181
+ /** Test seam — forgets every adapter, load and consent answer. */
182
+ export declare function resetAnalyticsProviders(): void;
@@ -0,0 +1,133 @@
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
+ */ const providers = new Map();
17
+ const listeners = new Set();
18
+ let snapshot = [];
19
+ let lastGrants = null;
20
+ function publish() {
21
+ snapshot = [
22
+ ...providers.values()
23
+ ];
24
+ for (const listener of [
25
+ ...listeners
26
+ ]){
27
+ try {
28
+ listener();
29
+ } catch (unused) {
30
+ // One listener cannot stop the others hearing about the provider.
31
+ }
32
+ }
33
+ }
34
+ /**
35
+ * Registers `pluginId`'s adapter. The same plugin registering again replaces
36
+ * its adapter. If a consent answer was already given in this document, the
37
+ * adapter is told it before anything else can reach it.
38
+ */ export function registerAnalyticsProvider(pluginId, provider) {
39
+ providers.set(pluginId, provider);
40
+ if (lastGrants) {
41
+ try {
42
+ provider.applyConsent(lastGrants);
43
+ } catch (unused) {
44
+ // An adapter that throws on its own consent call is one we cannot
45
+ // silence; the cookie sweep still stands.
46
+ }
47
+ }
48
+ publish();
49
+ }
50
+ /** The registered adapters, in registration order. A stable array. */ export function analyticsProviders() {
51
+ return snapshot;
52
+ }
53
+ /** Hears every registration. Answers the unsubscribe. */ export function subscribeAnalyticsProviders(listener) {
54
+ listeners.add(listener);
55
+ return ()=>{
56
+ listeners.delete(listener);
57
+ };
58
+ }
59
+ /**
60
+ * Tell every resident tag the visitor's answer, and answer the ids acted on.
61
+ * Advertising is clamped to analytics: a refusal withdraws both.
62
+ */ export function applyAnalyticsConsent(grants) {
63
+ const analytics = grants.analytics === true;
64
+ const clamped = {
65
+ analytics,
66
+ advertising: analytics && grants.advertising === true
67
+ };
68
+ lastGrants = clamped;
69
+ const ids = [];
70
+ for (const provider of snapshot){
71
+ try {
72
+ ids.push(...provider.applyConsent(clamped));
73
+ } catch (unused) {
74
+ // See `registerAnalyticsProvider`.
75
+ }
76
+ }
77
+ return ids;
78
+ }
79
+ /** Whether any registered adapter has a tag resident that can take an event. */ export function analyticsTagResident() {
80
+ return snapshot.some((provider)=>{
81
+ try {
82
+ return provider.resident();
83
+ } catch (unused) {
84
+ return false;
85
+ }
86
+ });
87
+ }
88
+ /**
89
+ * Hand one event to every resident tag. Answers whether any took it, so a
90
+ * caller that may hold an event while a tag is still loading knows to.
91
+ */ export function sendAnalyticsProviderEvent(name, params, options) {
92
+ let sent = false;
93
+ for (const provider of snapshot){
94
+ try {
95
+ if (!provider.resident()) continue;
96
+ provider.sendEvent(name, params, options);
97
+ sent = true;
98
+ } catch (unused) {
99
+ // Analytics never breaks the page.
100
+ }
101
+ }
102
+ return sent;
103
+ }
104
+ const loading = new Map();
105
+ /**
106
+ * Fetch and register each adapter once per document. Idempotent and safe to
107
+ * call during render: it starts the fetch, never waits for it, and a loader
108
+ * that fails leaves that provider unregistered — the vendor's tag then never
109
+ * mounts, which is the outcome a failed vendor library has anyway.
110
+ */ export function loadAnalyticsProviders(loaders) {
111
+ return Promise.all(loaders.map((loader)=>{
112
+ let pending = loading.get(loader.pluginId);
113
+ if (!pending) {
114
+ pending = loader.load().then((module)=>{
115
+ if (module == null ? void 0 : module.analyticsProvider) {
116
+ registerAnalyticsProvider(loader.pluginId, module.analyticsProvider);
117
+ }
118
+ }).catch(()=>{
119
+ // No adapter, no tag — see above.
120
+ });
121
+ loading.set(loader.pluginId, pending);
122
+ }
123
+ return pending;
124
+ })).then(()=>undefined);
125
+ }
126
+ /** Test seam — forgets every adapter, load and consent answer. */ export function resetAnalyticsProviders() {
127
+ providers.clear();
128
+ loading.clear();
129
+ lastGrants = null;
130
+ publish();
131
+ }
132
+
133
+ //# sourceMappingURL=analytics-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/analytics-provider.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 { VisitorConsentHost } from './visitor-consent'\n\n/**\n * Third-party analytics tags, behind one contract (AGL-3080).\n *\n * A site can send its visitors' measurement to an analytics vendor it chose:\n * a measurement id or a tag container, saved in the site's analytics\n * settings. The platform owns everything around that tag — whether this\n * visitor may be measured at all (`visitor-consent.ts`), the consent banner,\n * the first-party pageview beacon that meters the site (`analytics-beacon.ts`,\n * which is not a tag and never goes through here) — and names no vendor. A\n * plugin's ADAPTER is what knows a vendor: what its tag looks like on the\n * page, how a resident tag is told the visitor's answer changed, and how an\n * event reaches it.\n *\n * ## Declared, then loaded where it is used\n *\n * Two questions, answered by two different mechanisms, because their readers\n * cannot wait for the same thing:\n *\n * - \"Does this site run a tag, so is there anything to ask its visitors?\" is\n * asked synchronously, during render, by the consent gate on the published\n * page and by the console's banner card and preview. A registry one of\n * those had not filled yet would answer \"no tag\" — no banner, and nothing to\n * gate — so the answer is COMPILED: the plugin declares which of the site's\n * analytics settings it mounts a tag for (`analyticsProvider.settings` in\n * `plugins.config.json`), and `hostConfiguresAnalyticsTag` in\n * `visitor-consent.ts` reads that list. With no provider declared, no setting configures a tag, and a site\n * with nothing that could mount asks its visitors nothing.\n * - Everything else — mounting the tag, telling it the answer changed,\n * handing it an event — happens after the adapter has loaded. The adapter\n * is a chunk of its own, reached through each app's generated\n * `plugins.analytics.generated.ts` and fetched only by a document that uses\n * it: a published page whose site configures a tag, and the console, whose\n * own measurement tag the consent controls answer to. A page whose site\n * configures none never fetches it.\n *\n * ## What an unloaded adapter means\n *\n * A tag is mounted only by its adapter, so until the adapter has loaded there\n * is no tag of its to silence or to send to, and nothing is lost by the wait:\n * events find no resident tag and drop, exactly as they do for a visitor who\n * has not granted.\n *\n * A tag this document did not mount — the console's, which its analytics SDK\n * injects — can be resident before the adapter arrives. So the last answer\n * {@link applyAnalyticsConsent} was given is kept, and an adapter that\n * registers later is handed it at once: a withdrawal made in that window\n * reaches the tag the moment something can speak to it.\n */\n\n/**\n * What a provider declares about itself, compiled from `plugins.config.json`.\n * `settings` are keys of the site's `analytics` settings; each is validated\n * by the pattern `ANALYTICS_SETTING_PATTERNS` in `visitor-consent.ts` holds\n * for it.\n */\nexport interface AnalyticsProviderDeclaration {\n /** The plugin that provides the adapter. */\n pluginId: string\n /** The analytics settings this provider mounts a tag for. */\n settings: readonly string[]\n}\n\n/** The visitor's answer, in the platform's two categories. */\nexport interface AnalyticsGrants {\n analytics: boolean\n advertising: boolean\n}\n\n/**\n * One tag on a published page: an inline boot that runs first, then the\n * vendor's library. The page renders the pair in order, under the consent\n * gate and with the request's nonce, as `${id}-init` and `${id}-src`.\n *\n * Both strings land in the document. `boot` is inline script, so an adapter\n * builds it from constants and format-checked ids only.\n */\nexport interface AnalyticsTagMount {\n /** Element id prefix. Stable: specs and the CSP report read it. */\n id: string\n /** Inline script that runs before the library. */\n boot: string\n /** The library URL. */\n src: string\n /**\n * A marker for the library this pair loads, for an advertising vendor that\n * rides the same library instead of fetching it again (`sharesLibrary` in\n * `advertising-tags.ts`).\n */\n library?: string\n}\n\n/** What the page tells an adapter about the pageview it mounts tags for. */\nexport interface AnalyticsMountOptions {\n /**\n * Whether the site runs the platform's consent machinery. When it does, the\n * tag is told the visitor's answer before it measures anything; when the\n * host runs a consent solution of its own, that solution tells it.\n */\n consentRequired: boolean\n /** Whether this visitor granted advertising storage on this site. */\n advertising: boolean\n}\n\nexport interface AnalyticsEventOptions {\n /**\n * Address only the measurement destinations of the resident tag, not any\n * advertising destination sharing its library. Set for events that no\n * advertising report reads, such as Core Web Vitals.\n */\n measurementOnly?: boolean\n}\n\n/** A vendor's adapter. */\nexport interface AnalyticsProvider {\n /**\n * The tags a granted pageview mounts for `host`, in order; empty when the\n * host configures none of this provider's settings, or configures them\n * malformed. Called only once the page's consent gate has said yes.\n */\n mounts(\n host: VisitorConsentHost | null | undefined,\n options: AnalyticsMountOptions,\n ): readonly AnalyticsTagMount[]\n /**\n * Make every resident tag of this vendor agree with `grants`, and answer\n * the ids it acted on. A page cannot unload a script, so a withdrawal in\n * the middle of a pageview has to be told to the tag that is already there.\n */\n applyConsent(grants: AnalyticsGrants): readonly string[]\n /** Whether a tag of this vendor is resident and can take an event now. */\n resident(): boolean\n /** Hand one event to the resident tag. Never throws. */\n sendEvent(\n name: string,\n params: Record<string, unknown>,\n options?: AnalyticsEventOptions,\n ): void\n}\n\n/** An adapter module, as an app's generated manifest loads it. */\nexport interface AnalyticsProviderModule {\n analyticsProvider: AnalyticsProvider\n}\n\n/** One row of an app's generated `plugins.analytics.generated.ts`. */\nexport interface AnalyticsProviderLoader {\n pluginId: string\n load: () => Promise<AnalyticsProviderModule>\n}\n\nconst providers = new Map<string, AnalyticsProvider>()\nconst listeners = new Set<() => void>()\nlet snapshot: readonly AnalyticsProvider[] = []\nlet lastGrants: AnalyticsGrants | null = null\n\nfunction publish(): void {\n snapshot = [...providers.values()]\n for (const listener of [...listeners]) {\n try {\n listener()\n } catch {\n // One listener cannot stop the others hearing about the provider.\n }\n }\n}\n\n/**\n * Registers `pluginId`'s adapter. The same plugin registering again replaces\n * its adapter. If a consent answer was already given in this document, the\n * adapter is told it before anything else can reach it.\n */\nexport function registerAnalyticsProvider(\n pluginId: string,\n provider: AnalyticsProvider,\n): void {\n providers.set(pluginId, provider)\n if (lastGrants) {\n try {\n provider.applyConsent(lastGrants)\n } catch {\n // An adapter that throws on its own consent call is one we cannot\n // silence; the cookie sweep still stands.\n }\n }\n publish()\n}\n\n/** The registered adapters, in registration order. A stable array. */\nexport function analyticsProviders(): readonly AnalyticsProvider[] {\n return snapshot\n}\n\n/** Hears every registration. Answers the unsubscribe. */\nexport function subscribeAnalyticsProviders(listener: () => void): () => void {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/**\n * Tell every resident tag the visitor's answer, and answer the ids acted on.\n * Advertising is clamped to analytics: a refusal withdraws both.\n */\nexport function applyAnalyticsConsent(grants: AnalyticsGrants): string[] {\n const analytics = grants.analytics === true\n const clamped: AnalyticsGrants = {\n analytics,\n advertising: analytics && grants.advertising === true,\n }\n lastGrants = clamped\n const ids: string[] = []\n for (const provider of snapshot) {\n try {\n ids.push(...provider.applyConsent(clamped))\n } catch {\n // See `registerAnalyticsProvider`.\n }\n }\n return ids\n}\n\n/** Whether any registered adapter has a tag resident that can take an event. */\nexport function analyticsTagResident(): boolean {\n return snapshot.some((provider) => {\n try {\n return provider.resident()\n } catch {\n return false\n }\n })\n}\n\n/**\n * Hand one event to every resident tag. Answers whether any took it, so a\n * caller that may hold an event while a tag is still loading knows to.\n */\nexport function sendAnalyticsProviderEvent(\n name: string,\n params: Record<string, unknown>,\n options?: AnalyticsEventOptions,\n): boolean {\n let sent = false\n for (const provider of snapshot) {\n try {\n if (!provider.resident()) continue\n provider.sendEvent(name, params, options)\n sent = true\n } catch {\n // Analytics never breaks the page.\n }\n }\n return sent\n}\n\nconst loading = new Map<string, Promise<void>>()\n\n/**\n * Fetch and register each adapter once per document. Idempotent and safe to\n * call during render: it starts the fetch, never waits for it, and a loader\n * that fails leaves that provider unregistered — the vendor's tag then never\n * mounts, which is the outcome a failed vendor library has anyway.\n */\nexport function loadAnalyticsProviders(\n loaders: readonly AnalyticsProviderLoader[],\n): Promise<void> {\n return Promise.all(\n loaders.map((loader) => {\n let pending = loading.get(loader.pluginId)\n if (!pending) {\n pending = loader\n .load()\n .then((module) => {\n if (module?.analyticsProvider) {\n registerAnalyticsProvider(loader.pluginId, module.analyticsProvider)\n }\n })\n .catch(() => {\n // No adapter, no tag — see above.\n })\n loading.set(loader.pluginId, pending)\n }\n return pending\n }),\n ).then((): void => undefined)\n}\n\n/** Test seam — forgets every adapter, load and consent answer. */\nexport function resetAnalyticsProviders(): void {\n providers.clear()\n loading.clear()\n lastGrants = null\n publish()\n}\n"],"names":["providers","Map","listeners","Set","snapshot","lastGrants","publish","values","listener","registerAnalyticsProvider","pluginId","provider","set","applyConsent","analyticsProviders","subscribeAnalyticsProviders","add","delete","applyAnalyticsConsent","grants","analytics","clamped","advertising","ids","push","analyticsTagResident","some","resident","sendAnalyticsProviderEvent","name","params","options","sent","sendEvent","loading","loadAnalyticsProviders","loaders","Promise","all","map","loader","pending","get","load","then","module","analyticsProvider","catch","undefined","resetAnalyticsProviders","clear"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GA0JD,MAAMA,YAAY,IAAIC;AACtB,MAAMC,YAAY,IAAIC;AACtB,IAAIC,WAAyC,EAAE;AAC/C,IAAIC,aAAqC;AAEzC,SAASC;IACPF,WAAW;WAAIJ,UAAUO,MAAM;KAAG;IAClC,KAAK,MAAMC,YAAY;WAAIN;KAAU,CAAE;QACrC,IAAI;YACFM;QACF,EAAE,eAAM;QACN,kEAAkE;QACpE;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASC,0BACdC,QAAgB,EAChBC,QAA2B;IAE3BX,UAAUY,GAAG,CAACF,UAAUC;IACxB,IAAIN,YAAY;QACd,IAAI;YACFM,SAASE,YAAY,CAACR;QACxB,EAAE,eAAM;QACN,kEAAkE;QAClE,0CAA0C;QAC5C;IACF;IACAC;AACF;AAEA,oEAAoE,GACpE,OAAO,SAASQ;IACd,OAAOV;AACT;AAEA,uDAAuD,GACvD,OAAO,SAASW,4BAA4BP,QAAoB;IAC9DN,UAAUc,GAAG,CAACR;IACd,OAAO;QACLN,UAAUe,MAAM,CAACT;IACnB;AACF;AAEA;;;CAGC,GACD,OAAO,SAASU,sBAAsBC,MAAuB;IAC3D,MAAMC,YAAYD,OAAOC,SAAS,KAAK;IACvC,MAAMC,UAA2B;QAC/BD;QACAE,aAAaF,aAAaD,OAAOG,WAAW,KAAK;IACnD;IACAjB,aAAagB;IACb,MAAME,MAAgB,EAAE;IACxB,KAAK,MAAMZ,YAAYP,SAAU;QAC/B,IAAI;YACFmB,IAAIC,IAAI,IAAIb,SAASE,YAAY,CAACQ;QACpC,EAAE,eAAM;QACN,mCAAmC;QACrC;IACF;IACA,OAAOE;AACT;AAEA,8EAA8E,GAC9E,OAAO,SAASE;IACd,OAAOrB,SAASsB,IAAI,CAAC,CAACf;QACpB,IAAI;YACF,OAAOA,SAASgB,QAAQ;QAC1B,EAAE,eAAM;YACN,OAAO;QACT;IACF;AACF;AAEA;;;CAGC,GACD,OAAO,SAASC,2BACdC,IAAY,EACZC,MAA+B,EAC/BC,OAA+B;IAE/B,IAAIC,OAAO;IACX,KAAK,MAAMrB,YAAYP,SAAU;QAC/B,IAAI;YACF,IAAI,CAACO,SAASgB,QAAQ,IAAI;YAC1BhB,SAASsB,SAAS,CAACJ,MAAMC,QAAQC;YACjCC,OAAO;QACT,EAAE,eAAM;QACN,mCAAmC;QACrC;IACF;IACA,OAAOA;AACT;AAEA,MAAME,UAAU,IAAIjC;AAEpB;;;;;CAKC,GACD,OAAO,SAASkC,uBACdC,OAA2C;IAE3C,OAAOC,QAAQC,GAAG,CAChBF,QAAQG,GAAG,CAAC,CAACC;QACX,IAAIC,UAAUP,QAAQQ,GAAG,CAACF,OAAO9B,QAAQ;QACzC,IAAI,CAAC+B,SAAS;YACZA,UAAUD,OACPG,IAAI,GACJC,IAAI,CAAC,CAACC;gBACL,IAAIA,0BAAAA,OAAQC,iBAAiB,EAAE;oBAC7BrC,0BAA0B+B,OAAO9B,QAAQ,EAAEmC,OAAOC,iBAAiB;gBACrE;YACF,GACCC,KAAK,CAAC;YACL,kCAAkC;YACpC;YACFb,QAAQtB,GAAG,CAAC4B,OAAO9B,QAAQ,EAAE+B;QAC/B;QACA,OAAOA;IACT,IACAG,IAAI,CAAC,IAAYI;AACrB;AAEA,gEAAgE,GAChE,OAAO,SAASC;IACdjD,UAAUkD,KAAK;IACfhB,QAAQgB,KAAK;IACb7C,aAAa;IACbC;AACF"}
@@ -137,6 +137,39 @@ export interface PluginApiRouteOptions {
137
137
  * authenticates the link itself, by a signature it verifies.
138
138
  */
139
139
  recipientLink?: boolean;
140
+ /**
141
+ * The route is called by a MACHINE that proves itself with a credential
142
+ * the route verifies — a scheduler's shared secret, a provider's signed
143
+ * webhook — and names no site and no member (AGL-3080).
144
+ *
145
+ * Such a request has no subject the dispatcher could gate on before the
146
+ * route has verified it: a sweep spans every organization, and a webhook's
147
+ * workspace is inside a payload only a verified signature makes worth
148
+ * reading. Gated anonymously, a plugin on a partial rollout would refuse
149
+ * every delivery. So both dispatchers skip their per-site enablement and
150
+ * release gates for it, and — because a chunked sweep posts from one
151
+ * address, and a provider retries in bursts — their write limit and their
152
+ * cross-origin check (`isMachinePluginApiPath`); lockdown still applies.
153
+ * The flag grants nothing by itself: the route authenticates its caller,
154
+ * and judges the plugin's release and plan for each organization it
155
+ * resolves from what it verified.
156
+ */
157
+ machine?: boolean;
158
+ /**
159
+ * The route hands a workspace the records a plugin keeps for it — a
160
+ * data-portability export — which the workspace is owed whether or not the
161
+ * plugin is switched on or released for it now (AGL-3080).
162
+ *
163
+ * Exporting the people a workspace holds is an obligation, not a feature:
164
+ * a workspace that switched the plugin off, or a plugin paused by its
165
+ * kill switch, still has to be able to take its data away. So both
166
+ * dispatchers skip their per-site enablement and release gates for it —
167
+ * and nothing else: lockdown, the email-verification gate and the rate
168
+ * limit still apply. The flag grants nothing by itself: the route
169
+ * authenticates the member, and asks the plugin's release and plan itself
170
+ * of anything it serves beyond what the workspace is owed.
171
+ */
172
+ portability?: boolean;
140
173
  /**
141
174
  * The route OPENS A CARD PAYMENT for whoever calls it: a Checkout Session,
142
175
  * a PaymentIntent or a SetupIntent (AGL-3363).
@@ -194,6 +227,19 @@ export declare function resolvePluginApiMatch(path: string): PluginApiMatch | un
194
227
  * before their enablement and release gates (AGL-2981).
195
228
  */
196
229
  export declare function isPluginRecipientLinkRoute(path: string): boolean;
230
+ /**
231
+ * Whether a request path resolves to a route registered as a
232
+ * {@link PluginApiRouteOptions.machine} caller's — what both dispatchers ask
233
+ * before their enablement and release gates, and the write limits before
234
+ * they count (AGL-3080).
235
+ */
236
+ export declare function isPluginMachineRoute(path: string): boolean;
237
+ /**
238
+ * Whether a request path resolves to a route registered as a
239
+ * {@link PluginApiRouteOptions.portability} export — what both dispatchers
240
+ * ask before their enablement and release gates (AGL-3080).
241
+ */
242
+ export declare function isPluginPortabilityRoute(path: string): boolean;
197
243
  /**
198
244
  * Whether a request path resolves to a route registered with
199
245
  * {@link PluginApiRouteOptions.cardPayment} — what the tenant dispatcher
@@ -157,6 +157,25 @@ export function unregisterPluginApiRoute(path) {
157
157
  const matched = matchRegisteredApiKey(path);
158
158
  return matched ? ((_apiRouteOptions_get = apiRouteOptions.get(matched.key)) == null ? void 0 : _apiRouteOptions_get.recipientLink) === true : false;
159
159
  }
160
+ /**
161
+ * Whether a request path resolves to a route registered as a
162
+ * {@link PluginApiRouteOptions.machine} caller's — what both dispatchers ask
163
+ * before their enablement and release gates, and the write limits before
164
+ * they count (AGL-3080).
165
+ */ export function isPluginMachineRoute(path) {
166
+ var _apiRouteOptions_get;
167
+ const matched = matchRegisteredApiKey(path);
168
+ return matched ? ((_apiRouteOptions_get = apiRouteOptions.get(matched.key)) == null ? void 0 : _apiRouteOptions_get.machine) === true : false;
169
+ }
170
+ /**
171
+ * Whether a request path resolves to a route registered as a
172
+ * {@link PluginApiRouteOptions.portability} export — what both dispatchers
173
+ * ask before their enablement and release gates (AGL-3080).
174
+ */ export function isPluginPortabilityRoute(path) {
175
+ var _apiRouteOptions_get;
176
+ const matched = matchRegisteredApiKey(path);
177
+ return matched ? ((_apiRouteOptions_get = apiRouteOptions.get(matched.key)) == null ? void 0 : _apiRouteOptions_get.portability) === true : false;
178
+ }
160
179
  /**
161
180
  * Whether a request path resolves to a route registered with
162
181
  * {@link PluginApiRouteOptions.cardPayment} — what the tenant dispatcher
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/api-plugins.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Server half of the plugin pattern (AGL-396): the API-route registry, the\n * server counterpart to the ConsoleExtension registry. A feature plugin\n * registers request handlers here from its `/server` entry point (never its\n * client barrel, so firebase-admin and other server-only deps stay out of\n * the browser bundle). The app ships one catch-all dispatcher route per\n * Next app that resolves a request path to a registered handler — so moving\n * a feature's API into its plugin needs no new app route and keeps the same\n * URL. Reference implementation: events-calendar `events/list` (AGL-396).\n *\n * The request/response shapes are structural (not `next` types) so plugins\n * stay framework-light; `NextApiRequest`/`NextApiResponse` satisfy them, so\n * the dispatcher passes Next's objects straight through.\n */\n\nexport interface PluginApiRequest {\n method?: string\n query: Partial<Record<string, string | string[]>>\n // `any` mirrors NextApiRequest.body (parsed JSON/form/text); handlers\n // validate it themselves, as they did as Next routes.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n body: any\n /**\n * The unparsed request text, for handlers that verify signatures over the\n * exact payload (Stripe/Svix webhooks registered as plugin API paths).\n * UTF-8 re-encoding of the text is byte-identical for valid UTF-8 JSON.\n */\n rawBody?: string\n headers: Partial<Record<string, string | string[]>>\n cookies: Partial<Record<string, string>>\n /**\n * Node socket — the client-address fallback (`remoteAddress`).\n *\n * On the console's Pages Router this is the real transport peer. Behind the\n * App Router adapter there is no socket, and `api-adapter.ts` fills it from\n * the shared client-address reader so a handler falling back to it gets the\n * same trusted hop rather than a caller-supplied one.\n */\n socket: { remoteAddress?: string }\n}\n\nexport interface PluginApiResponse {\n status(code: number): PluginApiResponse\n json(body: unknown): void\n send(body: unknown): void\n setHeader(name: string, value: string | number | readonly string[]): void\n redirect(url: string): void\n redirect(status: number, url: string): void\n end(): void\n}\n\nexport type PluginApiHandler = (\n req: PluginApiRequest,\n res: PluginApiResponse,\n) => void | Promise<void>\n\n/**\n * A handler in the Web shape (AGL-2939): the dispatcher's own `Request`\n * in, a `Response` out — with a streamed body when the door streams, as\n * the assistant's chat and a job's event feed do. `params` carries the\n * dispatcher's path segments and any `:name` segment the registered path\n * declared.\n */\nexport type PluginWebApiHandler = (\n request: Request,\n context: { params: Record<string, string | string[]> },\n) => Response | Promise<Response>\n\n/** What a route registers: the (req, res) shape, or a Web handler. */\nexport type PluginApiRoute = PluginApiHandler | { web: PluginWebApiHandler }\n\n/** A registered route matched to a request path, with its `:name` segments filled. */\nexport interface PluginApiMatch {\n route: PluginApiRoute\n params: Record<string, string>\n}\n\n/**\n * Who a request is FOR, as far as the dispatchers' release gate is concerned\n * (AGL-2978).\n *\n * The gate buckets a rollout and reads per-org overrides on an org id, and a\n * dispatcher learns that id from the site a request names (`hostId`). Two\n * kinds of request name no site and still belong to one organization:\n *\n * - a request to an ORGANIZATION-level surface (`ConsoleExtension.orgNavItems`,\n * AGL-2974), which names its org rather than a site;\n * - a request that arrives with no bearer token by design — a provider's\n * OAuth redirect back to the platform, which carries only what the platform\n * signed into it.\n *\n * Without a subject the gate treats both as anonymous, so a plugin released to\n * one organization by override refused that organization's own requests, and\n * a staff member previewing a dark plugin could not complete a redirect their\n * own session had started.\n */\nexport interface PluginApiRequestSubject {\n /** The organization: the rollout bucket and the override key. */\n orgId: string | null\n /**\n * The account the request acts for, read ONLY when the request carries no\n * bearer token — a token speaks for itself. A route may name one only when\n * it verified a signature binding the uid to the request, because the gate\n * lets a staff account named here preview a released-off plugin.\n */\n uid?: string | null\n}\n\n/** Reads a request's subject; answers `null` when the request names none. */\nexport type PluginApiSubjectResolver = (\n request: Request,\n) => PluginApiRequestSubject | null | Promise<PluginApiRequestSubject | null>\n\n/** What a registration may say about its route beyond the handler. */\nexport interface PluginApiRouteOptions {\n /**\n * How a request to this route names its subject when it names no site.\n * Consulted by both dispatchers before their release gate, and only for a\n * request with no `hostId`: a named site always decides the org.\n */\n subject?: PluginApiSubjectResolver\n /**\n * The route answers a link the platform MAILED to somebody — an\n * unsubscribe in a message's `List-Unsubscribe` header — rather than a door\n * a member uses (AGL-2981).\n *\n * Such a link has to keep working whether or not its plugin is released,\n * or switched on, for the workspace NOW. A recipient's way out does not\n * close when a rollout is paused or a workspace turns the plugin off:\n * CAN-SPAM holds an opt-out open for thirty days after the send, and an\n * unsubscribe that 404s the day a kill switch is flipped is one nobody can\n * use. So both dispatchers skip their per-site enablement and release\n * gates for it — and nothing else: lockdown and the rate limit still\n * apply. The flag says nothing about who may act, so the route\n * authenticates the link itself, by a signature it verifies.\n */\n recipientLink?: boolean\n /**\n * The route OPENS A CARD PAYMENT for whoever calls it: a Checkout Session,\n * a PaymentIntent or a SetupIntent (AGL-3363).\n *\n * The tenant dispatcher holds such a route to the card-testing counters\n * (`card-payment-velocity.ts`) on top of its ordinary write limit, so a\n * plugin's payment door is covered by declaring itself here rather than by\n * carrying a limiter of its own. A console door (a signed-in merchant's\n * POS or draft order) does not declare it: a register ringing up a queue\n * is not a card tester. The 3-D Secure sweep\n * (`card-payments-request-3ds.spec.ts`) classifies every file that creates\n * a payment, so a new visitor door cannot skip the declaration.\n */\n cardPayment?: boolean\n}\n\n/** Leading/trailing slashes stripped so '/events/list' and 'events/list' key alike. */\nfunction normalizeApiPath(path: string): string {\n return path.replace(/^\\/+|\\/+$/g, '')\n}\n\nconst apiRoutes = new Map<string, PluginApiRoute>()\nconst apiRouteOwners = new Map<string, string>()\nconst apiRouteOptions = new Map<string, PluginApiRouteOptions>()\n\n/**\n * The \"currently registering plugin\" marker moved to a leaf module\n * (`registering-plugin`) so `site-page-hooks` can read it too — this file is\n * only reachable through the `/server` entry, and importing it from the shared\n * plugin-manager barrel formed a cycle (AGL-1289). Re-exported here so the\n * plugin loader's existing import keeps working unchanged.\n */\nimport { getRegisteringPluginId } from './registering-plugin'\n\nexport {\n setRegisteringPluginId,\n getRegisteringPluginId,\n} from './registering-plugin'\n\n/**\n * Registers a plugin API handler at a host-relative path (e.g.\n * 'events/list'), served by the app dispatcher at `/api/events/list`.\n *\n * Idempotent by path FOR ITS OWNER — re-registration by the plugin that\n * holds the path replaces the previous handler, which is what a hot reload,\n * a second surface, and a repeated init all depend on.\n *\n * A different plugin is REFUSED (AGL-2484). This map is process-global and\n * was last-writer-wins on the path alone, while Aglyn's own commission-taking\n * handlers sit at `marketplace/checkout` and `commerce/checkout` — and the\n * tenant dispatcher calls `ensureRemoteServerBundles()` after the first-party\n * loader has run, so a remote bundle registering either path was simply the\n * later writer. Getting there needs `PLUGIN_REMOTE_SERVER=enabled` (off by\n * default), realm trust and a valid signature, so this is the last of several\n * doors rather than the only one; it is also the one the registry could close\n * by itself, because it already recorded who registered what.\n *\n * Anonymous registration — no loader marker — is an IDENTITY here, not a\n * wildcard: it may replace itself and nothing else may take it.\n */\nexport function registerPluginApiRoute(\n path: string,\n handler: PluginApiRoute,\n options?: PluginApiRouteOptions,\n): void {\n const key = normalizeApiPath(path)\n const owner = getRegisteringPluginId() ?? ANONYMOUS_OWNER\n const incumbent = apiRouteOwners.get(key)\n if (incumbent !== undefined && incumbent !== owner) {\n // Loud, because the silent version of this is a payment route quietly\n // answering from someone else's code. Refusing is the safe direction:\n // the incumbent keeps serving.\n console.error(\n `[plugins] refused API route \"${key}\" to \"${owner}\": already ` +\n `registered by \"${incumbent}\"`,\n )\n return\n }\n apiRoutes.set(key, handler)\n apiRouteOwners.set(key, owner)\n // Replaced with the handler, never merged: a re-registration that drops its\n // subject resolver must not keep answering with the previous one.\n if (options) apiRouteOptions.set(key, options)\n else apiRouteOptions.delete(key)\n}\n\n/** The marker for a registration made outside any loader context. */\nconst ANONYMOUS_OWNER = ''\n\n/** The plugin that registered a path, for per-org enablement gating. */\nexport function pluginIdForRegisteredApiPath(path: string): string | undefined {\n const owner = apiRouteOwners.get(normalizeApiPath(path))\n // An anonymous registration has no plugin to gate on, and this has always\n // answered `undefined` for one — the sentinel must not leak out as an id.\n return owner === undefined || owner === ANONYMOUS_OWNER ? undefined : owner\n}\n\nexport function unregisterPluginApiRoute(path: string): void {\n const key = normalizeApiPath(path)\n apiRoutes.delete(key)\n // Release the CLAIM too, or the refusal above outlives the route and the\n // path can never be re-registered for the life of the process.\n apiRouteOwners.delete(key)\n apiRouteOptions.delete(key)\n}\n\n/**\n * The registered key a request path resolves to, with its `:name` segments.\n *\n * An exact registration wins; otherwise a registration whose path carries\n * `:name` segments matches segment for segment. One matcher for the route and\n * for its options, so a request can never be answered by one registration and\n * gated with another's subject.\n */\nfunction matchRegisteredApiKey(\n path: string,\n): { key: string; params: Record<string, string> } | undefined {\n const key = normalizeApiPath(path)\n if (apiRoutes.has(key)) return { key, params: {} }\n const segments = key.split('/')\n for (const registered of apiRoutes.keys()) {\n if (!registered.includes(':')) continue\n const pattern = registered.split('/')\n if (pattern.length !== segments.length) continue\n const params: Record<string, string> = {}\n let matched = true\n for (let index = 0; index < pattern.length; index += 1) {\n const part = pattern[index]\n if (part.startsWith(':') && segments[index]) {\n params[part.slice(1)] = segments[index]\n } else if (part !== segments[index]) {\n matched = false\n break\n }\n }\n if (matched) return { key: registered, params }\n }\n return undefined\n}\n\n/**\n * The route for a request path, or undefined when nothing is registered.\n *\n * An exact registration wins; otherwise a registration whose path carries\n * `:name` segments matches segment for segment (`ai/jobs/:jobId/cancel`\n * answers `ai/jobs/abc/cancel`), and the named segments come back as\n * `params`. Patterns are tried in registration order and a static segment\n * beats a named one only by being registered exactly.\n */\nexport function resolvePluginApiMatch(path: string): PluginApiMatch | undefined {\n const matched = matchRegisteredApiKey(path)\n if (!matched) return undefined\n const route = apiRoutes.get(matched.key)\n return route ? { route, params: matched.params } : undefined\n}\n\n/**\n * Whether a request path resolves to a route registered as a\n * {@link PluginApiRouteOptions.recipientLink} — what both dispatchers ask\n * before their enablement and release gates (AGL-2981).\n */\nexport function isPluginRecipientLinkRoute(path: string): boolean {\n const matched = matchRegisteredApiKey(path)\n return matched ? apiRouteOptions.get(matched.key)?.recipientLink === true : false\n}\n\n/**\n * Whether a request path resolves to a route registered with\n * {@link PluginApiRouteOptions.cardPayment} — what the tenant dispatcher\n * asks before its card-testing counters (AGL-3363).\n */\nexport function isPluginCardPaymentRoute(path: string): boolean {\n const matched = matchRegisteredApiKey(path)\n return matched ? apiRouteOptions.get(matched.key)?.cardPayment === true : false\n}\n\n/** An id a subject may carry: a non-empty path segment, not a path. */\nconst SUBJECT_ID = /^[^/\\s]{1,128}$/\n\nconst subjectId = (value: unknown): string | null =>\n typeof value === 'string' && SUBJECT_ID.test(value) ? value : null\n\n/**\n * The subject a request to a registered route names, or `null` (AGL-2978).\n *\n * `null` when the route declared no resolver, when the resolver answered\n * nothing, and when it THREW: a resolver that cannot read its request has\n * named no subject, and the gate then treats the request as anonymous — the\n * conservative direction. The resolver reads a clone, so the handler still\n * receives the untouched body. Ids are held to a plain path segment, so a\n * resolver cannot hand the gate a path to read overrides from.\n */\nexport async function resolvePluginApiRequestSubject(\n path: string,\n request: Request,\n): Promise<PluginApiRequestSubject | null> {\n const matched = matchRegisteredApiKey(path)\n const resolve = matched ? apiRouteOptions.get(matched.key)?.subject : undefined\n if (!resolve) return null\n try {\n const subject = await resolve(request.clone())\n if (!subject) return null\n const orgId = subjectId(subject.orgId)\n const uid = subjectId(subject.uid)\n if (!orgId && !uid) return null\n return { orgId, ...(uid ? { uid } : {}) }\n } catch {\n return null\n }\n}\n\n/**\n * The legacy (node-style) handler at a request path, or undefined when\n * nothing is registered there or the route is a web handler — a web route\n * is reached through `resolvePluginApiMatch` and `runPluginApiMatch`, since\n * it takes the `Request` and the filled `:name` segments, not a `req`/`res`\n * pair.\n */\nexport function resolvePluginApiRoute(path: string): PluginApiHandler | undefined {\n const route = resolvePluginApiMatch(path)?.route\n return typeof route === 'function' ? route : undefined\n}\n\n/**\n * Runs a matched route against the dispatcher's request: a Web handler is\n * called as it is, and a (req, res) handler goes through the adapter the\n * caller supplies — the adapter lives on the `/server` entry, which this\n * module may not import.\n */\nexport async function runPluginApiMatch(\n match: PluginApiMatch,\n request: Request,\n params: Record<string, string | string[]>,\n runLegacy: (\n handler: PluginApiHandler,\n request: Request,\n params: Record<string, string | string[]>,\n ) => Promise<Response>,\n): Promise<Response> {\n const merged = { ...params, ...match.params }\n const route = match.route as { web?: PluginWebApiHandler }\n if (typeof route === 'object' && route !== null && typeof route.web === 'function') {\n return route.web(request, { params: merged })\n }\n return runLegacy(match.route as PluginApiHandler, request, merged)\n}\n\n/** Registered paths, for diagnostics. */\nexport function listPluginApiRoutes(): string[] {\n return Array.from(apiRoutes.keys())\n}\n"],"names":["normalizeApiPath","path","replace","apiRoutes","Map","apiRouteOwners","apiRouteOptions","getRegisteringPluginId","setRegisteringPluginId","registerPluginApiRoute","handler","options","key","owner","ANONYMOUS_OWNER","incumbent","get","undefined","console","error","set","delete","pluginIdForRegisteredApiPath","unregisterPluginApiRoute","matchRegisteredApiKey","has","params","segments","split","registered","keys","includes","pattern","length","matched","index","part","startsWith","slice","resolvePluginApiMatch","route","isPluginRecipientLinkRoute","recipientLink","isPluginCardPaymentRoute","cardPayment","SUBJECT_ID","subjectId","value","test","resolvePluginApiRequestSubject","request","resolve","subject","clone","orgId","uid","resolvePluginApiRoute","runPluginApiMatch","match","runLegacy","merged","web","listPluginApiRoutes","Array","from"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/api-plugins.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Server half of the plugin pattern (AGL-396): the API-route registry, the\n * server counterpart to the ConsoleExtension registry. A feature plugin\n * registers request handlers here from its `/server` entry point (never its\n * client barrel, so firebase-admin and other server-only deps stay out of\n * the browser bundle). The app ships one catch-all dispatcher route per\n * Next app that resolves a request path to a registered handler — so moving\n * a feature's API into its plugin needs no new app route and keeps the same\n * URL. Reference implementation: events-calendar `events/list` (AGL-396).\n *\n * The request/response shapes are structural (not `next` types) so plugins\n * stay framework-light; `NextApiRequest`/`NextApiResponse` satisfy them, so\n * the dispatcher passes Next's objects straight through.\n */\n\nexport interface PluginApiRequest {\n method?: string\n query: Partial<Record<string, string | string[]>>\n // `any` mirrors NextApiRequest.body (parsed JSON/form/text); handlers\n // validate it themselves, as they did as Next routes.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n body: any\n /**\n * The unparsed request text, for handlers that verify signatures over the\n * exact payload (Stripe/Svix webhooks registered as plugin API paths).\n * UTF-8 re-encoding of the text is byte-identical for valid UTF-8 JSON.\n */\n rawBody?: string\n headers: Partial<Record<string, string | string[]>>\n cookies: Partial<Record<string, string>>\n /**\n * Node socket — the client-address fallback (`remoteAddress`).\n *\n * On the console's Pages Router this is the real transport peer. Behind the\n * App Router adapter there is no socket, and `api-adapter.ts` fills it from\n * the shared client-address reader so a handler falling back to it gets the\n * same trusted hop rather than a caller-supplied one.\n */\n socket: { remoteAddress?: string }\n}\n\nexport interface PluginApiResponse {\n status(code: number): PluginApiResponse\n json(body: unknown): void\n send(body: unknown): void\n setHeader(name: string, value: string | number | readonly string[]): void\n redirect(url: string): void\n redirect(status: number, url: string): void\n end(): void\n}\n\nexport type PluginApiHandler = (\n req: PluginApiRequest,\n res: PluginApiResponse,\n) => void | Promise<void>\n\n/**\n * A handler in the Web shape (AGL-2939): the dispatcher's own `Request`\n * in, a `Response` out — with a streamed body when the door streams, as\n * the assistant's chat and a job's event feed do. `params` carries the\n * dispatcher's path segments and any `:name` segment the registered path\n * declared.\n */\nexport type PluginWebApiHandler = (\n request: Request,\n context: { params: Record<string, string | string[]> },\n) => Response | Promise<Response>\n\n/** What a route registers: the (req, res) shape, or a Web handler. */\nexport type PluginApiRoute = PluginApiHandler | { web: PluginWebApiHandler }\n\n/** A registered route matched to a request path, with its `:name` segments filled. */\nexport interface PluginApiMatch {\n route: PluginApiRoute\n params: Record<string, string>\n}\n\n/**\n * Who a request is FOR, as far as the dispatchers' release gate is concerned\n * (AGL-2978).\n *\n * The gate buckets a rollout and reads per-org overrides on an org id, and a\n * dispatcher learns that id from the site a request names (`hostId`). Two\n * kinds of request name no site and still belong to one organization:\n *\n * - a request to an ORGANIZATION-level surface (`ConsoleExtension.orgNavItems`,\n * AGL-2974), which names its org rather than a site;\n * - a request that arrives with no bearer token by design — a provider's\n * OAuth redirect back to the platform, which carries only what the platform\n * signed into it.\n *\n * Without a subject the gate treats both as anonymous, so a plugin released to\n * one organization by override refused that organization's own requests, and\n * a staff member previewing a dark plugin could not complete a redirect their\n * own session had started.\n */\nexport interface PluginApiRequestSubject {\n /** The organization: the rollout bucket and the override key. */\n orgId: string | null\n /**\n * The account the request acts for, read ONLY when the request carries no\n * bearer token — a token speaks for itself. A route may name one only when\n * it verified a signature binding the uid to the request, because the gate\n * lets a staff account named here preview a released-off plugin.\n */\n uid?: string | null\n}\n\n/** Reads a request's subject; answers `null` when the request names none. */\nexport type PluginApiSubjectResolver = (\n request: Request,\n) => PluginApiRequestSubject | null | Promise<PluginApiRequestSubject | null>\n\n/** What a registration may say about its route beyond the handler. */\nexport interface PluginApiRouteOptions {\n /**\n * How a request to this route names its subject when it names no site.\n * Consulted by both dispatchers before their release gate, and only for a\n * request with no `hostId`: a named site always decides the org.\n */\n subject?: PluginApiSubjectResolver\n /**\n * The route answers a link the platform MAILED to somebody — an\n * unsubscribe in a message's `List-Unsubscribe` header — rather than a door\n * a member uses (AGL-2981).\n *\n * Such a link has to keep working whether or not its plugin is released,\n * or switched on, for the workspace NOW. A recipient's way out does not\n * close when a rollout is paused or a workspace turns the plugin off:\n * CAN-SPAM holds an opt-out open for thirty days after the send, and an\n * unsubscribe that 404s the day a kill switch is flipped is one nobody can\n * use. So both dispatchers skip their per-site enablement and release\n * gates for it — and nothing else: lockdown and the rate limit still\n * apply. The flag says nothing about who may act, so the route\n * authenticates the link itself, by a signature it verifies.\n */\n recipientLink?: boolean\n /**\n * The route is called by a MACHINE that proves itself with a credential\n * the route verifies — a scheduler's shared secret, a provider's signed\n * webhook — and names no site and no member (AGL-3080).\n *\n * Such a request has no subject the dispatcher could gate on before the\n * route has verified it: a sweep spans every organization, and a webhook's\n * workspace is inside a payload only a verified signature makes worth\n * reading. Gated anonymously, a plugin on a partial rollout would refuse\n * every delivery. So both dispatchers skip their per-site enablement and\n * release gates for it, and — because a chunked sweep posts from one\n * address, and a provider retries in bursts — their write limit and their\n * cross-origin check (`isMachinePluginApiPath`); lockdown still applies.\n * The flag grants nothing by itself: the route authenticates its caller,\n * and judges the plugin's release and plan for each organization it\n * resolves from what it verified.\n */\n machine?: boolean\n /**\n * The route hands a workspace the records a plugin keeps for it — a\n * data-portability export — which the workspace is owed whether or not the\n * plugin is switched on or released for it now (AGL-3080).\n *\n * Exporting the people a workspace holds is an obligation, not a feature:\n * a workspace that switched the plugin off, or a plugin paused by its\n * kill switch, still has to be able to take its data away. So both\n * dispatchers skip their per-site enablement and release gates for it —\n * and nothing else: lockdown, the email-verification gate and the rate\n * limit still apply. The flag grants nothing by itself: the route\n * authenticates the member, and asks the plugin's release and plan itself\n * of anything it serves beyond what the workspace is owed.\n */\n portability?: boolean\n /**\n * The route OPENS A CARD PAYMENT for whoever calls it: a Checkout Session,\n * a PaymentIntent or a SetupIntent (AGL-3363).\n *\n * The tenant dispatcher holds such a route to the card-testing counters\n * (`card-payment-velocity.ts`) on top of its ordinary write limit, so a\n * plugin's payment door is covered by declaring itself here rather than by\n * carrying a limiter of its own. A console door (a signed-in merchant's\n * POS or draft order) does not declare it: a register ringing up a queue\n * is not a card tester. The 3-D Secure sweep\n * (`card-payments-request-3ds.spec.ts`) classifies every file that creates\n * a payment, so a new visitor door cannot skip the declaration.\n */\n cardPayment?: boolean\n}\n\n/** Leading/trailing slashes stripped so '/events/list' and 'events/list' key alike. */\nfunction normalizeApiPath(path: string): string {\n return path.replace(/^\\/+|\\/+$/g, '')\n}\n\nconst apiRoutes = new Map<string, PluginApiRoute>()\nconst apiRouteOwners = new Map<string, string>()\nconst apiRouteOptions = new Map<string, PluginApiRouteOptions>()\n\n/**\n * The \"currently registering plugin\" marker moved to a leaf module\n * (`registering-plugin`) so `site-page-hooks` can read it too — this file is\n * only reachable through the `/server` entry, and importing it from the shared\n * plugin-manager barrel formed a cycle (AGL-1289). Re-exported here so the\n * plugin loader's existing import keeps working unchanged.\n */\nimport { getRegisteringPluginId } from './registering-plugin'\n\nexport {\n setRegisteringPluginId,\n getRegisteringPluginId,\n} from './registering-plugin'\n\n/**\n * Registers a plugin API handler at a host-relative path (e.g.\n * 'events/list'), served by the app dispatcher at `/api/events/list`.\n *\n * Idempotent by path FOR ITS OWNER — re-registration by the plugin that\n * holds the path replaces the previous handler, which is what a hot reload,\n * a second surface, and a repeated init all depend on.\n *\n * A different plugin is REFUSED (AGL-2484). This map is process-global and\n * was last-writer-wins on the path alone, while Aglyn's own commission-taking\n * handlers sit at `marketplace/checkout` and `commerce/checkout` — and the\n * tenant dispatcher calls `ensureRemoteServerBundles()` after the first-party\n * loader has run, so a remote bundle registering either path was simply the\n * later writer. Getting there needs `PLUGIN_REMOTE_SERVER=enabled` (off by\n * default), realm trust and a valid signature, so this is the last of several\n * doors rather than the only one; it is also the one the registry could close\n * by itself, because it already recorded who registered what.\n *\n * Anonymous registration — no loader marker — is an IDENTITY here, not a\n * wildcard: it may replace itself and nothing else may take it.\n */\nexport function registerPluginApiRoute(\n path: string,\n handler: PluginApiRoute,\n options?: PluginApiRouteOptions,\n): void {\n const key = normalizeApiPath(path)\n const owner = getRegisteringPluginId() ?? ANONYMOUS_OWNER\n const incumbent = apiRouteOwners.get(key)\n if (incumbent !== undefined && incumbent !== owner) {\n // Loud, because the silent version of this is a payment route quietly\n // answering from someone else's code. Refusing is the safe direction:\n // the incumbent keeps serving.\n console.error(\n `[plugins] refused API route \"${key}\" to \"${owner}\": already ` +\n `registered by \"${incumbent}\"`,\n )\n return\n }\n apiRoutes.set(key, handler)\n apiRouteOwners.set(key, owner)\n // Replaced with the handler, never merged: a re-registration that drops its\n // subject resolver must not keep answering with the previous one.\n if (options) apiRouteOptions.set(key, options)\n else apiRouteOptions.delete(key)\n}\n\n/** The marker for a registration made outside any loader context. */\nconst ANONYMOUS_OWNER = ''\n\n/** The plugin that registered a path, for per-org enablement gating. */\nexport function pluginIdForRegisteredApiPath(path: string): string | undefined {\n const owner = apiRouteOwners.get(normalizeApiPath(path))\n // An anonymous registration has no plugin to gate on, and this has always\n // answered `undefined` for one — the sentinel must not leak out as an id.\n return owner === undefined || owner === ANONYMOUS_OWNER ? undefined : owner\n}\n\nexport function unregisterPluginApiRoute(path: string): void {\n const key = normalizeApiPath(path)\n apiRoutes.delete(key)\n // Release the CLAIM too, or the refusal above outlives the route and the\n // path can never be re-registered for the life of the process.\n apiRouteOwners.delete(key)\n apiRouteOptions.delete(key)\n}\n\n/**\n * The registered key a request path resolves to, with its `:name` segments.\n *\n * An exact registration wins; otherwise a registration whose path carries\n * `:name` segments matches segment for segment. One matcher for the route and\n * for its options, so a request can never be answered by one registration and\n * gated with another's subject.\n */\nfunction matchRegisteredApiKey(\n path: string,\n): { key: string; params: Record<string, string> } | undefined {\n const key = normalizeApiPath(path)\n if (apiRoutes.has(key)) return { key, params: {} }\n const segments = key.split('/')\n for (const registered of apiRoutes.keys()) {\n if (!registered.includes(':')) continue\n const pattern = registered.split('/')\n if (pattern.length !== segments.length) continue\n const params: Record<string, string> = {}\n let matched = true\n for (let index = 0; index < pattern.length; index += 1) {\n const part = pattern[index]\n if (part.startsWith(':') && segments[index]) {\n params[part.slice(1)] = segments[index]\n } else if (part !== segments[index]) {\n matched = false\n break\n }\n }\n if (matched) return { key: registered, params }\n }\n return undefined\n}\n\n/**\n * The route for a request path, or undefined when nothing is registered.\n *\n * An exact registration wins; otherwise a registration whose path carries\n * `:name` segments matches segment for segment (`ai/jobs/:jobId/cancel`\n * answers `ai/jobs/abc/cancel`), and the named segments come back as\n * `params`. Patterns are tried in registration order and a static segment\n * beats a named one only by being registered exactly.\n */\nexport function resolvePluginApiMatch(path: string): PluginApiMatch | undefined {\n const matched = matchRegisteredApiKey(path)\n if (!matched) return undefined\n const route = apiRoutes.get(matched.key)\n return route ? { route, params: matched.params } : undefined\n}\n\n/**\n * Whether a request path resolves to a route registered as a\n * {@link PluginApiRouteOptions.recipientLink} — what both dispatchers ask\n * before their enablement and release gates (AGL-2981).\n */\nexport function isPluginRecipientLinkRoute(path: string): boolean {\n const matched = matchRegisteredApiKey(path)\n return matched ? apiRouteOptions.get(matched.key)?.recipientLink === true : false\n}\n\n/**\n * Whether a request path resolves to a route registered as a\n * {@link PluginApiRouteOptions.machine} caller's — what both dispatchers ask\n * before their enablement and release gates, and the write limits before\n * they count (AGL-3080).\n */\nexport function isPluginMachineRoute(path: string): boolean {\n const matched = matchRegisteredApiKey(path)\n return matched ? apiRouteOptions.get(matched.key)?.machine === true : false\n}\n\n/**\n * Whether a request path resolves to a route registered as a\n * {@link PluginApiRouteOptions.portability} export — what both dispatchers\n * ask before their enablement and release gates (AGL-3080).\n */\nexport function isPluginPortabilityRoute(path: string): boolean {\n const matched = matchRegisteredApiKey(path)\n return matched ? apiRouteOptions.get(matched.key)?.portability === true : false\n}\n\n/**\n * Whether a request path resolves to a route registered with\n * {@link PluginApiRouteOptions.cardPayment} — what the tenant dispatcher\n * asks before its card-testing counters (AGL-3363).\n */\nexport function isPluginCardPaymentRoute(path: string): boolean {\n const matched = matchRegisteredApiKey(path)\n return matched ? apiRouteOptions.get(matched.key)?.cardPayment === true : false\n}\n\n/** An id a subject may carry: a non-empty path segment, not a path. */\nconst SUBJECT_ID = /^[^/\\s]{1,128}$/\n\nconst subjectId = (value: unknown): string | null =>\n typeof value === 'string' && SUBJECT_ID.test(value) ? value : null\n\n/**\n * The subject a request to a registered route names, or `null` (AGL-2978).\n *\n * `null` when the route declared no resolver, when the resolver answered\n * nothing, and when it THREW: a resolver that cannot read its request has\n * named no subject, and the gate then treats the request as anonymous — the\n * conservative direction. The resolver reads a clone, so the handler still\n * receives the untouched body. Ids are held to a plain path segment, so a\n * resolver cannot hand the gate a path to read overrides from.\n */\nexport async function resolvePluginApiRequestSubject(\n path: string,\n request: Request,\n): Promise<PluginApiRequestSubject | null> {\n const matched = matchRegisteredApiKey(path)\n const resolve = matched ? apiRouteOptions.get(matched.key)?.subject : undefined\n if (!resolve) return null\n try {\n const subject = await resolve(request.clone())\n if (!subject) return null\n const orgId = subjectId(subject.orgId)\n const uid = subjectId(subject.uid)\n if (!orgId && !uid) return null\n return { orgId, ...(uid ? { uid } : {}) }\n } catch {\n return null\n }\n}\n\n/**\n * The legacy (node-style) handler at a request path, or undefined when\n * nothing is registered there or the route is a web handler — a web route\n * is reached through `resolvePluginApiMatch` and `runPluginApiMatch`, since\n * it takes the `Request` and the filled `:name` segments, not a `req`/`res`\n * pair.\n */\nexport function resolvePluginApiRoute(path: string): PluginApiHandler | undefined {\n const route = resolvePluginApiMatch(path)?.route\n return typeof route === 'function' ? route : undefined\n}\n\n/**\n * Runs a matched route against the dispatcher's request: a Web handler is\n * called as it is, and a (req, res) handler goes through the adapter the\n * caller supplies — the adapter lives on the `/server` entry, which this\n * module may not import.\n */\nexport async function runPluginApiMatch(\n match: PluginApiMatch,\n request: Request,\n params: Record<string, string | string[]>,\n runLegacy: (\n handler: PluginApiHandler,\n request: Request,\n params: Record<string, string | string[]>,\n ) => Promise<Response>,\n): Promise<Response> {\n const merged = { ...params, ...match.params }\n const route = match.route as { web?: PluginWebApiHandler }\n if (typeof route === 'object' && route !== null && typeof route.web === 'function') {\n return route.web(request, { params: merged })\n }\n return runLegacy(match.route as PluginApiHandler, request, merged)\n}\n\n/** Registered paths, for diagnostics. */\nexport function listPluginApiRoutes(): string[] {\n return Array.from(apiRoutes.keys())\n}\n"],"names":["normalizeApiPath","path","replace","apiRoutes","Map","apiRouteOwners","apiRouteOptions","getRegisteringPluginId","setRegisteringPluginId","registerPluginApiRoute","handler","options","key","owner","ANONYMOUS_OWNER","incumbent","get","undefined","console","error","set","delete","pluginIdForRegisteredApiPath","unregisterPluginApiRoute","matchRegisteredApiKey","has","params","segments","split","registered","keys","includes","pattern","length","matched","index","part","startsWith","slice","resolvePluginApiMatch","route","isPluginRecipientLinkRoute","recipientLink","isPluginMachineRoute","machine","isPluginPortabilityRoute","portability","isPluginCardPaymentRoute","cardPayment","SUBJECT_ID","subjectId","value","test","resolvePluginApiRequestSubject","request","resolve","subject","clone","orgId","uid","resolvePluginApiRoute","runPluginApiMatch","match","runLegacy","merged","web","listPluginApiRoutes","Array","from"],"mappings":"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"}
@@ -14,11 +14,11 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { type ResolvedProvenance } from './marketplace-provenance';
17
+ import { type ResolvedProvenance } from './artifact-provenance';
18
18
  /**
19
19
  * The override layer (AGL-1019) — own the patch, never the copy.
20
20
  *
21
- * Customising a marketplace artifact and updating it are only compatible if
21
+ * Customising an installed artifact and updating it are only compatible if
22
22
  * they are two different documents. Today they are one: you change a theme's
23
23
  * primary colour and that edits the theme, so the publisher's v2 can be taken
24
24
  * only by discarding your colour, or refused only by missing their fix.
@@ -34,10 +34,13 @@ import { type ResolvedProvenance } from './marketplace-provenance';
34
34
  * * a conflict is narrow: only where the patch touches a path the update also
35
35
  * changed, which the marketplace plugin's three-way update plan already names.
36
36
  *
37
- * Pure and content-shaped, exactly like the merge module it sits next to: it
38
- * knows nothing about Firestore or artifact types, so one resolver serves a
39
- * theme, a node tree and a plugin settings map, and every rule below is
40
- * testable without a database.
37
+ * Pure and content-shaped: it knows nothing about Firestore, artifact types
38
+ * or who installed the artifact, so one resolver serves a theme, a node tree
39
+ * and a plugin settings map, and every rule below is testable without a
40
+ * database. The platform's rather than the installer's (AGL-3080), because the
41
+ * readers are: the site's theme resolution, the theme editor and its library.
42
+ * The three-way update plan that writes a new base under a patch is the
43
+ * installer's own (the marketplace's `model/artifact-merge.ts`).
41
44
  *
42
45
  * ## Which "base" a reader resolves against
43
46
  *
@@ -14,11 +14,11 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */ import { stableStringify } from "./marketplace-provenance.js";
17
+ */ import { stableStringify } from "./artifact-provenance.js";
18
18
  /**
19
19
  * The override layer (AGL-1019) — own the patch, never the copy.
20
20
  *
21
- * Customising a marketplace artifact and updating it are only compatible if
21
+ * Customising an installed artifact and updating it are only compatible if
22
22
  * they are two different documents. Today they are one: you change a theme's
23
23
  * primary colour and that edits the theme, so the publisher's v2 can be taken
24
24
  * only by discarding your colour, or refused only by missing their fix.
@@ -34,10 +34,13 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
34
34
  * * a conflict is narrow: only where the patch touches a path the update also
35
35
  * changed, which the marketplace plugin's three-way update plan already names.
36
36
  *
37
- * Pure and content-shaped, exactly like the merge module it sits next to: it
38
- * knows nothing about Firestore or artifact types, so one resolver serves a
39
- * theme, a node tree and a plugin settings map, and every rule below is
40
- * testable without a database.
37
+ * Pure and content-shaped: it knows nothing about Firestore, artifact types
38
+ * or who installed the artifact, so one resolver serves a theme, a node tree
39
+ * and a plugin settings map, and every rule below is testable without a
40
+ * database. The platform's rather than the installer's (AGL-3080), because the
41
+ * readers are: the site's theme resolution, the theme editor and its library.
42
+ * The three-way update plan that writes a new base under a patch is the
43
+ * installer's own (the marketplace's `model/artifact-merge.ts`).
41
44
  *
42
45
  * ## Which "base" a reader resolves against
43
46
  *
@@ -459,4 +462,4 @@ function resolveKeyedArray(base, patch) {
459
462
  return cursor;
460
463
  }
461
464
 
462
- //# sourceMappingURL=marketplace-overrides.js.map
465
+ //# sourceMappingURL=artifact-overrides.js.map