@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
@@ -1,147 +0,0 @@
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
- * Who is allowed to see a marketplace listing (AGL-432/658/968/1196).
18
- *
19
- * These four predicates live in core for the same reason
20
- * `MarketplaceArtifactType` (AGL-1016) and the verification policy (AGL-1217)
21
- * do: a `scope:app` project may not depend on an `aglyn:addons` lib, and the
22
- * console now has to ask the question outside the marketplace plugin. The
23
- * listing route's `generateMetadata` (AGL-876) builds a social card from the
24
- * listing document, and that card is emitted to an UNAUTHENTICATED fetcher —
25
- * so it has to apply the same visibility rule the browse grid applies, or a
26
- * rejected, private or taken-down listing gets its name and description
27
- * unfurled into a chat message by anyone holding the URL.
28
- *
29
- * They were moved rather than copied. A second copy of "is this listing
30
- * public" is the failure mode this file exists to prevent: the rule already
31
- * gates browse AND install (AGL-965 added the second call site after the
32
- * first proved insufficient on its own), and a third reading that drifts
33
- * would be a disclosure bug, not a cosmetic one.
34
- *
35
- * `libs/plugins/marketplace/src/lib/model/marketplace.ts` re-exports all four,
36
- * so publishing and install code keeps importing them from one place.
37
- */ /**
38
- * The listing's artifact type, tolerating the pre-AGL-654 shape.
39
- *
40
- * Legacy listings carry `type`/`kind` instead; a component was the absence
41
- * of both, which is why this defaults there rather than throwing. Keeping
42
- * the fallback means old docs keep resolving correctly instead of silently
43
- * becoming un-installable.
44
- */ export function listingArtifactType(listing) {
45
- if (listing.artifactType) return listing.artifactType;
46
- if (listing.kind === 'template') return 'template';
47
- if (listing.type === 'plugin') return 'plugin';
48
- return 'component';
49
- }
50
- /**
51
- * Private plugins (AGL-968): an org builds for its own sites without
52
- * publishing to anyone. Never browsable — not even for the owner, who
53
- * reaches it through their own plugins area — and installable only by the
54
- * owning org (enforced in `install-plugin`, not in the UI). The review bar
55
- * is identical: private code still runs on our infrastructure.
56
- */ export function isPrivateListing(listing) {
57
- return listing.visibility === 'private';
58
- }
59
- /**
60
- * Whether a listing has been soft-deleted (AGL-1196).
61
- *
62
- * Browse used to express this as `where('deletedAt','==',null)` in the query
63
- * itself. That put a MUTABLE field in a predicate: `deletedAt` flips on every
64
- * unpublish/republish, and a document that stops matching a live query can
65
- * leave a `noDocument` tombstone at its own path — which the detail page then
66
- * reads BY ID and 404s on (AGL-827, AGL-929). Filtering in memory removes the
67
- * mechanism instead of healing it.
68
- *
69
- * FALSY, not `=== null`. Firestore's `== null` matches only an explicit null
70
- * and cannot express "field is absent", so a listing written without the field
71
- * was invisible to the old query. Absent means live.
72
- *
73
- * Deliberately NOT folded into `isListingBrowsable`: that check carries an
74
- * owner exemption, so a publisher can watch their own submission move through
75
- * review. Deletion has no such exemption — a deleted listing is gone for
76
- * everyone, including the org that owns it.
77
- */ export function isListingDeleted(listing) {
78
- return Boolean(listing.deletedAt);
79
- }
80
- /** Whether a plugin listing is publicly browsable (AGL-432). */ export function isListingBrowsable(listing) {
81
- // Staff takedown applies to EVERY artifact type (AGL-658). Pre-publication
82
- // review is plugin-only — plugins execute code, so they earn the wait —
83
- // but a component or template that turns out to be abusive must be
84
- // removable too, and before this it simply was not: the early return
85
- // below meant anything non-plugin was permanently browsable.
86
- if (listing.hiddenAt) return false;
87
- // The publishing workspace is locked (AGL-3365), for any reason: its
88
- // listings leave browse, search and their pages until the lift. Its own
89
- // field rather than `hiddenAt`, which is staff's takedown and also stops
90
- // already-installed plugins from loading — a lock over an unpaid bill must
91
- // not break a buyer's site, and the lift must not undo a takedown.
92
- if (listing.workspaceLockedAt) return false;
93
- // Private listings never reach the marketplace (AGL-968).
94
- if (isPrivateListing(listing)) return false;
95
- if (listingArtifactType(listing) !== 'plugin') return true;
96
- return listing.reviewStatus === undefined || listing.reviewStatus === 'listed' || listing.reviewStatus === 'verified';
97
- }
98
- /**
99
- * Listing fields a publisher may legitimately write, each with the reason
100
- * (AGL-1361). Everything else must be denied in the rules' listing key diff.
101
- *
102
- * They live here rather than beside `MarketplaceListing` for the same reason
103
- * the visibility policy does: this module is what READS the fields that decide
104
- * whether a listing is browsable, and `scope:app` may not depend on
105
- * `aglyn:addons`. `marketplace.ts` re-exports them so publishing code keeps
106
- * one import site.
107
- *
108
- * The list is short on purpose. Nothing in the repo writes a listing
109
- * document client-side any more: the unpublish/republish toggle in the org
110
- * seller panel was the last, and it goes through the publish API since
111
- * AGL-3321 — as publish, content edits, review and install always did. So
112
- * the fields below are classified writable because a publisher OWNS them,
113
- * not because anything would break: denying the rest cost nothing, which is
114
- * how AGL-1364 closed.
115
- */ export const LISTING_CLIENT_WRITABLE_FIELDS = {
116
- // `displayName` was here until AGL-3321: the marketplace lists search it
117
- // through keys the server derives, so it is written through the API
118
- // (`update-listing`) alone and denied here with `deletedAt` and
119
- // `visibility`, the two other fields a listing's browse audience follows.
120
- // `description`, `readme`, `homepageUrl`, `repositoryUrl` and `license`
121
- // were here as "publisher-authored, sanitized at render" (AGL-430). Since
122
- // AGL-3365 the API runs what a buyer reads through the phishing screen
123
- // before storing it, and a client write would publish it unscreened, so
124
- // they are written through `update-listing` alone and denied here.
125
- category: 'The single legacy category. Publisher-chosen shelf placement; it ' + 'orders a browse page and gates nothing.',
126
- categories: 'The AGL-430 multi-category list. Same reasoning as `category` — ' + 'placement, not permission.',
127
- // `logoUrl` and `screenshots` were here, justified as "sanitized at
128
- // render" (AGL-1701). They were not: listing-content.component.tsx hands
129
- // both straight to an `<img src>`, and `logoUrl` additionally becomes an
130
- // `og:image` served to unauthenticated unfurlers. Publisher-authored
131
- // ARTWORK is not like publisher-authored COPY — copy is read by the
132
- // viewer, artwork is FETCHED by the viewer's browser from whatever host
133
- // the string names, disclosing their IP and a `Referer` naming the
134
- // console and their org to a recipient on no subprocessor register.
135
- // Denied in the rules and held to first-party media on the API path.
136
- pluginId: 'The manifest id a plugin listing declares (AGL-45). Rewriting it ' + 'breaks the publisher\'s own install resolution; the artifact that ' + 'actually runs is pinned by sha256 in the staff-only `pluginVersions`.'
137
- };
138
- /**
139
- * Listing keys that never reach Firestore, so the rules have no opinion.
140
- *
141
- * Empty today — `MarketplaceListing` declares no `$id`, because listing code
142
- * carries the id alongside the data rather than folding it in. Kept as the
143
- * partition's third arm so a future projection field has a home that is not
144
- * "client-writable".
145
- */ export const LISTING_UNPERSISTED_FIELDS = {};
146
-
147
- //# sourceMappingURL=marketplace-listing-visibility.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/marketplace-listing-visibility.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 * Who is allowed to see a marketplace listing (AGL-432/658/968/1196).\n *\n * These four predicates live in core for the same reason\n * `MarketplaceArtifactType` (AGL-1016) and the verification policy (AGL-1217)\n * do: a `scope:app` project may not depend on an `aglyn:addons` lib, and the\n * console now has to ask the question outside the marketplace plugin. The\n * listing route's `generateMetadata` (AGL-876) builds a social card from the\n * listing document, and that card is emitted to an UNAUTHENTICATED fetcher —\n * so it has to apply the same visibility rule the browse grid applies, or a\n * rejected, private or taken-down listing gets its name and description\n * unfurled into a chat message by anyone holding the URL.\n *\n * They were moved rather than copied. A second copy of \"is this listing\n * public\" is the failure mode this file exists to prevent: the rule already\n * gates browse AND install (AGL-965 added the second call site after the\n * first proved insufficient on its own), and a third reading that drifts\n * would be a disclosure bug, not a cosmetic one.\n *\n * `libs/plugins/marketplace/src/lib/model/marketplace.ts` re-exports all four,\n * so publishing and install code keeps importing them from one place.\n */\n\nimport type { MarketplaceArtifactType } from './marketplace-provenance'\n\n/**\n * The listing's artifact type, tolerating the pre-AGL-654 shape.\n *\n * Legacy listings carry `type`/`kind` instead; a component was the absence\n * of both, which is why this defaults there rather than throwing. Keeping\n * the fallback means old docs keep resolving correctly instead of silently\n * becoming un-installable.\n */\nexport function listingArtifactType(listing: {\n artifactType?: string\n type?: string\n kind?: string\n}): MarketplaceArtifactType {\n if (listing.artifactType) return listing.artifactType as MarketplaceArtifactType\n if (listing.kind === 'template') return 'template'\n if (listing.type === 'plugin') return 'plugin'\n return 'component'\n}\n\n/**\n * Private plugins (AGL-968): an org builds for its own sites without\n * publishing to anyone. Never browsable — not even for the owner, who\n * reaches it through their own plugins area — and installable only by the\n * owning org (enforced in `install-plugin`, not in the UI). The review bar\n * is identical: private code still runs on our infrastructure.\n */\nexport function isPrivateListing(listing: { visibility?: string }): boolean {\n return listing.visibility === 'private'\n}\n\n/**\n * Whether a listing has been soft-deleted (AGL-1196).\n *\n * Browse used to express this as `where('deletedAt','==',null)` in the query\n * itself. That put a MUTABLE field in a predicate: `deletedAt` flips on every\n * unpublish/republish, and a document that stops matching a live query can\n * leave a `noDocument` tombstone at its own path — which the detail page then\n * reads BY ID and 404s on (AGL-827, AGL-929). Filtering in memory removes the\n * mechanism instead of healing it.\n *\n * FALSY, not `=== null`. Firestore's `== null` matches only an explicit null\n * and cannot express \"field is absent\", so a listing written without the field\n * was invisible to the old query. Absent means live.\n *\n * Deliberately NOT folded into `isListingBrowsable`: that check carries an\n * owner exemption, so a publisher can watch their own submission move through\n * review. Deletion has no such exemption — a deleted listing is gone for\n * everyone, including the org that owns it.\n */\nexport function isListingDeleted(listing: { deletedAt?: unknown }): boolean {\n return Boolean(listing.deletedAt)\n}\n\n/** Whether a plugin listing is publicly browsable (AGL-432). */\nexport function isListingBrowsable(listing: {\n artifactType?: string\n type?: string\n kind?: string\n reviewStatus?: string\n hiddenAt?: unknown\n visibility?: string\n workspaceLockedAt?: unknown\n}): boolean {\n // Staff takedown applies to EVERY artifact type (AGL-658). Pre-publication\n // review is plugin-only — plugins execute code, so they earn the wait —\n // but a component or template that turns out to be abusive must be\n // removable too, and before this it simply was not: the early return\n // below meant anything non-plugin was permanently browsable.\n if (listing.hiddenAt) return false\n // The publishing workspace is locked (AGL-3365), for any reason: its\n // listings leave browse, search and their pages until the lift. Its own\n // field rather than `hiddenAt`, which is staff's takedown and also stops\n // already-installed plugins from loading — a lock over an unpaid bill must\n // not break a buyer's site, and the lift must not undo a takedown.\n if (listing.workspaceLockedAt) return false\n // Private listings never reach the marketplace (AGL-968).\n if (isPrivateListing(listing)) return false\n if (listingArtifactType(listing) !== 'plugin') return true\n return (\n listing.reviewStatus === undefined ||\n listing.reviewStatus === 'listed' ||\n listing.reviewStatus === 'verified'\n )\n}\n\n/**\n * Listing fields a publisher may legitimately write, each with the reason\n * (AGL-1361). Everything else must be denied in the rules' listing key diff.\n *\n * They live here rather than beside `MarketplaceListing` for the same reason\n * the visibility policy does: this module is what READS the fields that decide\n * whether a listing is browsable, and `scope:app` may not depend on\n * `aglyn:addons`. `marketplace.ts` re-exports them so publishing code keeps\n * one import site.\n *\n * The list is short on purpose. Nothing in the repo writes a listing\n * document client-side any more: the unpublish/republish toggle in the org\n * seller panel was the last, and it goes through the publish API since\n * AGL-3321 — as publish, content edits, review and install always did. So\n * the fields below are classified writable because a publisher OWNS them,\n * not because anything would break: denying the rest cost nothing, which is\n * how AGL-1364 closed.\n */\nexport const LISTING_CLIENT_WRITABLE_FIELDS: Readonly<Record<string, string>> =\n {\n // `displayName` was here until AGL-3321: the marketplace lists search it\n // through keys the server derives, so it is written through the API\n // (`update-listing`) alone and denied here with `deletedAt` and\n // `visibility`, the two other fields a listing's browse audience follows.\n // `description`, `readme`, `homepageUrl`, `repositoryUrl` and `license`\n // were here as \"publisher-authored, sanitized at render\" (AGL-430). Since\n // AGL-3365 the API runs what a buyer reads through the phishing screen\n // before storing it, and a client write would publish it unscreened, so\n // they are written through `update-listing` alone and denied here.\n category:\n 'The single legacy category. Publisher-chosen shelf placement; it ' +\n 'orders a browse page and gates nothing.',\n categories:\n 'The AGL-430 multi-category list. Same reasoning as `category` — ' +\n 'placement, not permission.',\n // `logoUrl` and `screenshots` were here, justified as \"sanitized at\n // render\" (AGL-1701). They were not: listing-content.component.tsx hands\n // both straight to an `<img src>`, and `logoUrl` additionally becomes an\n // `og:image` served to unauthenticated unfurlers. Publisher-authored\n // ARTWORK is not like publisher-authored COPY — copy is read by the\n // viewer, artwork is FETCHED by the viewer's browser from whatever host\n // the string names, disclosing their IP and a `Referer` naming the\n // console and their org to a recipient on no subprocessor register.\n // Denied in the rules and held to first-party media on the API path.\n pluginId:\n 'The manifest id a plugin listing declares (AGL-45). Rewriting it ' +\n 'breaks the publisher\\'s own install resolution; the artifact that ' +\n 'actually runs is pinned by sha256 in the staff-only `pluginVersions`.',\n }\n\n/**\n * Listing keys that never reach Firestore, so the rules have no opinion.\n *\n * Empty today — `MarketplaceListing` declares no `$id`, because listing code\n * carries the id alongside the data rather than folding it in. Kept as the\n * partition's third arm so a future projection field has a home that is not\n * \"client-writable\".\n */\nexport const LISTING_UNPERSISTED_FIELDS: Readonly<Record<string, string>> = {}\n"],"names":["listingArtifactType","listing","artifactType","kind","type","isPrivateListing","visibility","isListingDeleted","Boolean","deletedAt","isListingBrowsable","hiddenAt","workspaceLockedAt","reviewStatus","undefined","LISTING_CLIENT_WRITABLE_FIELDS","category","categories","pluginId","LISTING_UNPERSISTED_FIELDS"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;CAqBC,GAID;;;;;;;CAOC,GACD,OAAO,SAASA,oBAAoBC,OAInC;IACC,IAAIA,QAAQC,YAAY,EAAE,OAAOD,QAAQC,YAAY;IACrD,IAAID,QAAQE,IAAI,KAAK,YAAY,OAAO;IACxC,IAAIF,QAAQG,IAAI,KAAK,UAAU,OAAO;IACtC,OAAO;AACT;AAEA;;;;;;CAMC,GACD,OAAO,SAASC,iBAAiBJ,OAAgC;IAC/D,OAAOA,QAAQK,UAAU,KAAK;AAChC;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,iBAAiBN,OAAgC;IAC/D,OAAOO,QAAQP,QAAQQ,SAAS;AAClC;AAEA,8DAA8D,GAC9D,OAAO,SAASC,mBAAmBT,OAQlC;IACC,2EAA2E;IAC3E,wEAAwE;IACxE,mEAAmE;IACnE,qEAAqE;IACrE,6DAA6D;IAC7D,IAAIA,QAAQU,QAAQ,EAAE,OAAO;IAC7B,qEAAqE;IACrE,wEAAwE;IACxE,yEAAyE;IACzE,2EAA2E;IAC3E,mEAAmE;IACnE,IAAIV,QAAQW,iBAAiB,EAAE,OAAO;IACtC,0DAA0D;IAC1D,IAAIP,iBAAiBJ,UAAU,OAAO;IACtC,IAAID,oBAAoBC,aAAa,UAAU,OAAO;IACtD,OACEA,QAAQY,YAAY,KAAKC,aACzBb,QAAQY,YAAY,KAAK,YACzBZ,QAAQY,YAAY,KAAK;AAE7B;AAEA;;;;;;;;;;;;;;;;;CAiBC,GACD,OAAO,MAAME,iCACX;IACE,yEAAyE;IACzE,oEAAoE;IACpE,gEAAgE;IAChE,0EAA0E;IAC1E,wEAAwE;IACxE,0EAA0E;IAC1E,uEAAuE;IACvE,wEAAwE;IACxE,mEAAmE;IACnEC,UACE,sEACA;IACFC,YACE,qEACA;IACF,oEAAoE;IACpE,yEAAyE;IACzE,yEAAyE;IACzE,qEAAqE;IACrE,oEAAoE;IACpE,wEAAwE;IACxE,mEAAmE;IACnE,oEAAoE;IACpE,qEAAqE;IACrEC,UACE,sEACA,uEACA;AACJ,EAAC;AAEH;;;;;;;CAOC,GACD,OAAO,MAAMC,6BAA+D,CAAC,EAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/marketplace-overrides.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n stableStringify,\n type ResolvedProvenance,\n} from './marketplace-provenance'\n\n/**\n * The override layer (AGL-1019) — own the patch, never the copy.\n *\n * Customising a marketplace artifact and updating it are only compatible if\n * they are two different documents. Today they are one: you change a theme's\n * primary colour and that edits the theme, so the publisher's v2 can be taken\n * only by discarding your colour, or refused only by missing their fix.\n *\n * Here the publisher's version stays untouched as the **base** — AGL-1015\n * already stores it, content-addressed and immutable, in\n * `marketplaceArtifactBases/{sha256}` — the user's changes are a **sparse\n * patch** stored beside it, and readers resolve `base ⊕ patch`. Then:\n *\n * * updating replaces the base wholesale and the patch still applies;\n * * \"what did I change\" IS the patch, with no diffing at read time;\n * * \"reset to the publisher's version\" is deleting the patch;\n * * a conflict is narrow: only where the patch touches a path the update also\n * changed, which the marketplace plugin's three-way update plan already names.\n *\n * Pure and content-shaped, exactly like the merge module it sits next to: it\n * knows nothing about Firestore or artifact types, so one resolver serves a\n * theme, a node tree and a plugin settings map, and every rule below is\n * testable without a database.\n *\n * ## Which \"base\" a reader resolves against\n *\n * The installed document's OWN content fields — whatever the install route\n * wrote — are the base at read time, and {@link resolveArtifactContent} takes\n * them directly. `marketplaceArtifactBases` is not that: it is closed to\n * clients in both directions (a base can hold paid content, and its key is on\n * the installed document, so a readable collection would hand out any listing's\n * content by hash). It is the immutable snapshot the server diffs and resets\n * against, nothing more.\n *\n * The two agree because an update writes the publisher's new version onto the\n * document and stamps a base at that same hash in one operation — which is the\n * property that makes \"never mutate the vendored artifact\" enforceable rather\n * than merely intended. The moment a user edit lands in those content fields\n * instead of in the override, both the diff and this resolver are wrong.\n *\n * ## Two rules that are load-bearing\n *\n * **Deletion needs a sentinel, and it is not `null`.** A sparse patch says\n * nothing about paths it omits, so \"omit it to remove it\" is unavailable —\n * omission already means \"inherit\". `null` cannot serve either: it is a real\n * value in this codebase's content, and the besigner's `null`-means-cleared\n * convention is what took every `/product/*` page down in AGL-1226. So removal\n * is spelled with {@link OVERRIDE_DELETE}, a value nothing else produces.\n *\n * **Arrays are never merged by index.** An insert at position 0 shifts every\n * element after it, so index-wise merging reports — and then writes — a change\n * to every one of them. Arrays therefore replace wholesale by default, which is\n * also how {@link planArtifactUpdate} compares them. Where elements carry a\n * stable identity the patch may instead use the keyed form\n * ({@link keyedArrayPatch}) and edit one element by key, so a publisher who\n * inserts an element does not collide with a user who edited a different one.\n */\n\n/**\n * The removal sentinel: a patch value of exactly this string removes the path\n * from the resolved output.\n *\n * A string rather than a wrapper object because a patch travels through\n * Firestore, JSON responses and React state unchanged, and a scalar survives\n * all three without anyone needing to know it is special. The cost is that an\n * artifact whose real content is this exact string cannot be expressed; that is\n * a deliberate trade for a sentinel that cannot be produced by accident, and\n * {@link diffOverride} is the only thing that emits it.\n *\n * Note this is a field *value*, never a field *name* — Firestore rejects field\n * names matching `__.*__`, and this deliberately has no trailing underscores so\n * that a careless refactor into a key still fails loudly rather than silently.\n */\nexport const OVERRIDE_DELETE = '__aglynOverrideDelete'\n\n/**\n * The field on an installed artifact document that holds its override.\n *\n * Beside the base, never inside it. The document's content fields keep holding\n * the publisher's version verbatim, which is what makes an update a wholesale\n * replacement of those fields and nothing else. Writing the user's edits into\n * the content instead is precisely the state this issue exists to leave.\n */\nexport const ARTIFACT_OVERRIDE_FIELD = 'overrides'\n\n/** The stored override, as written under {@link ARTIFACT_OVERRIDE_FIELD}. */\nexport interface ArtifactOverride {\n /** The sparse patch. Resolved over the artifact's content at read time. */\n patch: unknown\n /**\n * The base the patch was authored against — `installedFrom.sha256`.\n *\n * Recorded, not enforced: a patch stays valid across an update by design, and\n * the only thing this answers is \"which publisher version was on screen when\n * these edits were made\", which is what a conflict report needs to be honest.\n */\n baseSha256: string | null\n updatedAt?: unknown\n updatedBy?: string | null\n}\n\n/**\n * A patch node that edits an array BY KEY instead of replacing it.\n *\n * Only ever produced for arrays whose elements are objects carrying a unique\n * value under one identity field, because that is the only case where \"the\n * third element\" and \"the element the publisher shipped third\" are different\n * questions with the same answer.\n */\nexport interface KeyedArrayPatch {\n /** Discriminator. Present only on this form. */\n keyedBy: string\n /** Per-key sub-patches; a value of {@link OVERRIDE_DELETE} removes the element. */\n entries: Record<string, unknown>\n /**\n * The full key order, when the user reordered the array.\n *\n * Absent when they did not — and absence is the point: an array with no\n * `order` lets a publisher insert an element and have it appear, while an\n * array with one pins the order the user chose. Keys present in the base but\n * missing from `order` keep their relative position at the end, so a stale\n * `order` never drops an element.\n */\n order?: string[]\n}\n\n/** Identity fields tried, in order, when deciding whether an array is keyed. */\nconst KEY_FIELDS = ['id', 'key', 'slug', 'name'] as const\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n value != null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n (Object.getPrototypeOf(value) === Object.prototype ||\n Object.getPrototypeOf(value) === null)\n\nconst same = (a: unknown, b: unknown): boolean =>\n stableStringify(a) === stableStringify(b)\n\nexport const isDeleteSentinel = (value: unknown): boolean =>\n value === OVERRIDE_DELETE\n\nexport function isKeyedArrayPatch(value: unknown): value is KeyedArrayPatch {\n return (\n isPlainObject(value) &&\n typeof value['keyedBy'] === 'string' &&\n isPlainObject(value['entries'])\n )\n}\n\n/**\n * The identity field this array is keyed by, or null when it is not keyed.\n *\n * Every element must be a plain object carrying the same field, and every value\n * must be a non-empty string that appears once. A single duplicate or missing\n * key makes the whole array unkeyed rather than partly keyed — a patch that can\n * address only some elements is worse than one that addresses none, because the\n * gaps are invisible at the call site.\n */\nexport function arrayKeyField(value: unknown): string | null {\n if (!Array.isArray(value) || !value.length) return null\n if (!value.every(isPlainObject)) return null\n for (const field of KEY_FIELDS) {\n const keys = value.map((entry) => (entry as Record<string, unknown>)[field])\n if (!keys.every((key) => typeof key === 'string' && key !== '')) continue\n if (new Set(keys as string[]).size !== keys.length) continue\n return field\n }\n return null\n}\n\n/** Builds a keyed-array patch, dropping it entirely when it says nothing. */\nfunction keyedArrayPatch(\n keyedBy: string,\n entries: Record<string, unknown>,\n order: string[] | undefined,\n): KeyedArrayPatch | undefined {\n if (!Object.keys(entries).length && !order) return undefined\n return { keyedBy, entries, ...(order ? { order } : {}) }\n}\n\n/**\n * `base ⊕ patch` — the resolved view every reader sees.\n *\n * Deep-merges plain objects; everything else the patch names replaces what the\n * base holds. The base is never mutated: each level is copied on write, so a\n * caller can resolve the same base against two patches and get two answers.\n *\n * A patch that names a path the base does not have simply adds it. That is what\n * makes the same function serve \"override a publisher's field\" and \"set a field\n * the publisher never shipped\", and it is why a resolved artifact can be richer\n * than its base rather than only a recolouring of it.\n */\nexport function resolveOverride<T = unknown>(base: unknown, patch: unknown): T {\n return resolve(base, patch) as T\n}\n\nfunction resolve(base: unknown, patch: unknown): unknown {\n if (patch === undefined) return base\n if (isDeleteSentinel(patch)) return undefined\n\n if (isKeyedArrayPatch(patch)) return resolveKeyedArray(base, patch)\n\n if (isPlainObject(patch) && isPlainObject(base)) {\n const merged: Record<string, unknown> = { ...base }\n for (const [key, value] of Object.entries(patch)) {\n const resolved = resolve(merged[key], value)\n if (resolved === undefined) delete merged[key]\n else merged[key] = resolved\n }\n return merged\n }\n\n // A patch object over a non-object base still applies — the patch wins, and\n // its own delete sentinels are honoured so that `{a: DELETE}` over `null`\n // resolves to `{}` rather than smuggling the sentinel into the output.\n if (isPlainObject(patch)) return resolve({}, patch)\n\n return patch\n}\n\nfunction resolveKeyedArray(base: unknown, patch: KeyedArrayPatch): unknown[] {\n const source = Array.isArray(base) ? base : []\n const keyOf = (entry: unknown): string | undefined => {\n const key = isPlainObject(entry) ? entry[patch.keyedBy] : undefined\n return typeof key === 'string' && key !== '' ? key : undefined\n }\n\n const resolved: Array<{ key: string | undefined; value: unknown }> = []\n for (const entry of source) {\n const key = keyOf(entry)\n // An unkeyed element in an otherwise keyed array is unaddressable, so it is\n // carried through untouched rather than dropped: the patch never claimed to\n // describe it.\n if (key === undefined || !(key in patch.entries)) {\n resolved.push({ key, value: entry })\n continue\n }\n const value = resolve(entry, patch.entries[key])\n if (value !== undefined) resolved.push({ key, value })\n }\n\n // Entries the base does not hold are additions, appended in patch order.\n const present = new Set(resolved.map((entry) => entry.key))\n for (const [key, value] of Object.entries(patch.entries)) {\n if (present.has(key) || isDeleteSentinel(value)) continue\n const added = resolve(undefined, value)\n if (added !== undefined) resolved.push({ key, value: added })\n }\n\n if (!patch.order) return resolved.map((entry) => entry.value)\n\n // Ordered: the listed keys first, in the order given, then everything the\n // order did not mention in its existing relative position. A key in `order`\n // that no longer exists is skipped rather than inserted as a hole.\n const rank = new Map(patch.order.map((key, index) => [key, index]))\n return resolved\n .map((entry, index) => ({\n ...entry,\n rank: entry.key != null && rank.has(entry.key)\n ? (rank.get(entry.key) as number)\n : patch.order!.length + index,\n }))\n .sort((a, b) => a.rank - b.rank)\n .map((entry) => entry.value)\n}\n\n/**\n * \"What did I change\" — the sparse patch that turns `base` into `edited`.\n *\n * The inverse of {@link resolveOverride}, and held to it:\n * `resolveOverride(base, diffOverride(base, edited))` equals `edited` for every\n * shape this codebase stores. That round trip is the module's central property\n * and the thing its tests exist to defend, because every consumer downstream —\n * themes, theme overrides, host variables, plugin settings — inherits it.\n *\n * Returns `undefined` when nothing changed, so an unedited artifact stores no\n * override at all rather than an empty husk that reads as \"customised\".\n */\nexport function diffOverride(base: unknown, edited: unknown): unknown {\n if (same(base, edited)) return undefined\n if (edited === undefined) return OVERRIDE_DELETE\n\n if (isPlainObject(base) && isPlainObject(edited)) {\n const patch: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(edited)) {\n const child = diffOverride(base[key], value)\n if (child !== undefined) patch[key] = child\n }\n for (const key of Object.keys(base)) {\n if (!(key in edited)) patch[key] = OVERRIDE_DELETE\n }\n return patch\n }\n\n if (Array.isArray(base) && Array.isArray(edited)) {\n const keyed = diffKeyedArray(base, edited)\n if (keyed) return keyed\n }\n\n return edited\n}\n\n/**\n * A per-element patch for two keyed arrays, or null when they are not both\n * keyed by the same field — in which case the caller falls back to replacing\n * the array wholesale, which is always correct if sometimes coarse.\n */\nfunction diffKeyedArray(\n base: unknown[],\n edited: unknown[],\n): KeyedArrayPatch | undefined | null {\n const field = arrayKeyField(base)\n if (!field || arrayKeyField(edited) !== field) return null\n\n const keyOf = (entry: unknown) =>\n String((entry as Record<string, unknown>)[field])\n const baseByKey = new Map(base.map((entry) => [keyOf(entry), entry]))\n const editedKeys = edited.map(keyOf)\n\n const entries: Record<string, unknown> = {}\n for (const entry of edited) {\n const key = keyOf(entry)\n const child = diffOverride(baseByKey.get(key), entry)\n if (child !== undefined) entries[key] = child\n }\n for (const key of baseByKey.keys()) {\n if (!editedKeys.includes(key)) entries[key] = OVERRIDE_DELETE\n }\n\n // Order is stored only when the resolver would not already produce it —\n // surviving elements in the base's order, then additions in the order they\n // appear. So the common case (a value edit, or an element appended) stores no\n // order and leaves the publisher free to insert elements of their own, while\n // a genuine reorder or a mid-list insert is pinned.\n const natural = [\n ...base.map(keyOf).filter((key) => editedKeys.includes(key)),\n ...editedKeys.filter((key) => !baseByKey.has(key)),\n ]\n const reordered = !same(natural, editedKeys)\n\n return keyedArrayPatch(field, entries, reordered ? editedKeys : undefined)\n}\n\n/**\n * Every path the patch touches, dotted, for \"what did I change\" in a UI and for\n * pairing an override against an update plan's conflicts — whose paths\n * {@link planArtifactUpdate} spells the same way.\n *\n * A keyed array contributes `field[key]` segments rather than indices, because\n * an index is exactly the thing that stops meaning the same element the moment\n * the publisher inserts one.\n */\nexport function overridePaths(patch: unknown, prefix = ''): string[] {\n if (patch === undefined) return []\n if (isKeyedArrayPatch(patch)) {\n return Object.entries(patch.entries).flatMap(([key, value]) =>\n overridePaths(value, `${prefix}[${key}]`),\n )\n }\n if (isPlainObject(patch) && !isDeleteSentinel(patch)) {\n const keys = Object.keys(patch)\n if (keys.length) {\n return keys.flatMap((key) =>\n overridePaths(patch[key], prefix ? `${prefix}.${key}` : key),\n )\n }\n }\n return prefix ? [prefix] : []\n}\n\n/**\n * True when the patch says nothing — the artifact is the publisher's, as shipped.\n *\n * `undefined` is the only absence. A `null` is a VALUE the user chose, and\n * calling it empty here would silently discard \"set this field to null\" on\n * every save — the same conflation between \"cleared\" and \"absent\" that took\n * every `/product/*` page down in AGL-1226.\n */\nexport function isEmptyOverride(patch: unknown): boolean {\n if (patch === undefined) return true\n if (isKeyedArrayPatch(patch)) {\n return !patch.order && Object.values(patch.entries).every(isEmptyOverride)\n }\n if (isDeleteSentinel(patch)) return false\n if (isPlainObject(patch)) return Object.values(patch).every(isEmptyOverride)\n return false\n}\n\n/**\n * Is this shape storable as a whole-artifact override?\n *\n * A patch is a *sparse* description of edits to a structured artifact, so its\n * top level is an object or a keyed array and nothing else. A stored scalar —\n * above all a top-level `null` — would resolve the entire artifact away, which\n * is never something a user did on purpose and always something a half-written\n * or hand-edited field can produce. Nested nulls stay values; only the root is\n * constrained, and constraining it is what keeps a junk field from blanking a\n * live site.\n */\nfunction isStorablePatch(patch: unknown): boolean {\n return (\n (isKeyedArrayPatch(patch) || isPlainObject(patch)) && !isEmptyOverride(patch)\n )\n}\n\nexport interface OverrideEligibility {\n ok: boolean\n /** Why not, in the words the surface refusing it should use. */\n reason?: string\n}\n\n/**\n * Can this artifact hold an override at all? (AGL-1015's provenance, answered.)\n *\n * An override is a patch over the publisher's version, so an artifact with no\n * recorded base has nothing to be a patch *of*. Storing one anyway would\n * produce a patch against whatever the document happened to hold — including\n * the user's own earlier edits — and the first update would resolve it against\n * a base it was never written for. Saying so plainly is the whole point of the\n * `updatable` flag; this is the sentence to put next to the disabled control.\n */\nexport function canOverrideArtifact(\n provenance: ResolvedProvenance | null | undefined,\n): OverrideEligibility {\n if (!provenance || provenance.state === 'unknown') {\n return {\n ok: false,\n reason:\n 'This was not installed from the marketplace, so there is no ' +\n 'publisher’s version to keep your changes separate from. Edit it ' +\n 'directly — it is already yours.',\n }\n }\n if (!provenance.updatable) {\n return {\n ok: false,\n reason:\n 'This was installed before update tracking recorded an original, so ' +\n 'there is nothing to layer your changes over. Re-installing it from ' +\n 'the listing records one, and your changes can be kept separately ' +\n 'from then on.',\n }\n }\n return { ok: true }\n}\n\n/**\n * The value to write under {@link ARTIFACT_OVERRIDE_FIELD} — or `null`, meaning\n * \"reset to the publisher's version\", when the patch says nothing.\n *\n * ## Write this WHOLESALE. Never with `merge: true`.\n *\n * Firestore's `merge: true` deep-merges maps, and a patch is a map, so merging\n * a new patch onto an old one takes their union: every path the user has ever\n * overridden stays overridden, and removing an override becomes impossible\n * through the one write that looks like it should do it. `merge: true` on the\n * *document* is fine and expected — it is this *field* that must be replaced,\n * which is what passing this value under a `FieldPath` to `update`, or under\n * `set(..., {mergeFields})`, does:\n *\n * ```ts\n * await ref.set(\n * { [ARTIFACT_OVERRIDE_FIELD]: overrideWriteValue(patch, sha256), updatedAt },\n * { mergeFields: [ARTIFACT_OVERRIDE_FIELD, 'updatedAt'] },\n * )\n * ```\n *\n * The `null` for an empty patch is deliberate over a field delete: a reader\n * that sees `null` knows the override was considered and is empty, which reads\n * the same as absent and needs no second code path.\n */\nexport function overrideWriteValue(\n patch: unknown,\n baseSha256: string | null,\n meta?: { updatedAt?: unknown; updatedBy?: string | null },\n): ArtifactOverride | null {\n if (!isStorablePatch(patch)) return null\n return {\n patch,\n baseSha256,\n ...(meta?.updatedAt !== undefined ? { updatedAt: meta.updatedAt } : {}),\n ...(meta?.updatedBy !== undefined ? { updatedBy: meta.updatedBy } : {}),\n }\n}\n\n/**\n * Reads a stored override off a document, tolerating the junk a client-writable\n * field can hold.\n *\n * Returns `undefined` for anything that is not a well-formed override, so a\n * corrupt field resolves to the publisher's version rather than throwing on a\n * page that would otherwise render.\n */\nexport function readArtifactOverride(\n doc: Record<string, unknown> | null | undefined,\n): ArtifactOverride | undefined {\n const stored = doc?.[ARTIFACT_OVERRIDE_FIELD]\n if (!isPlainObject(stored)) return undefined\n if (!isStorablePatch(stored['patch'])) return undefined\n const sha = stored['baseSha256']\n return {\n patch: stored['patch'],\n baseSha256: typeof sha === 'string' && sha ? sha : null,\n ...(stored['updatedAt'] !== undefined ? { updatedAt: stored['updatedAt'] } : {}),\n ...(typeof stored['updatedBy'] === 'string'\n ? { updatedBy: stored['updatedBy'] }\n : {}),\n }\n}\n\n/**\n * The one call a reader makes: the publisher's content with this document's\n * override resolved over it.\n *\n * `content` is the artifact's own fields — whatever shape the install route\n * wrote, unchanged — so a caller never has to know whether an override exists.\n */\nexport function resolveArtifactContent<T = unknown>(\n content: unknown,\n doc: Record<string, unknown> | null | undefined,\n): T {\n const override = readArtifactOverride(doc)\n return (override ? resolveOverride(content, override.patch) : content) as T\n}\n\n/**\n * The paths where an incoming update collides with what the user overrode.\n *\n * Narrow by construction, which is the payoff the whole layer was built for: a\n * patch touching four paths can conflict in at most four places no matter how\n * much of the artifact the publisher rewrote, so the question put to the user is\n * \"the publisher also changed your heading colour — whose wins?\" rather than\n * \"this update rewrites 300 nodes, continue?\".\n *\n * A path whose incoming value already equals what the override sets is not a\n * conflict — the publisher adopted the user's change, and there is nothing to\n * ask about.\n */\nexport function overrideConflicts(\n base: unknown,\n patch: unknown,\n incoming: unknown,\n): string[] {\n const overridden = resolveOverride(base, patch)\n return overridePaths(patch).filter((path) => {\n const baseValue = readPath(base, path)\n const incomingValue = readPath(incoming, path)\n if (same(baseValue, incomingValue)) return false\n return !same(readPath(overridden, path), incomingValue)\n })\n}\n\n/** Reads a dotted path, understanding the `field[key]` segments keyed arrays use. */\nfunction readPath(value: unknown, path: string): unknown {\n let cursor: unknown = value\n for (const segment of path.match(/[^.[\\]]+/g) ?? []) {\n if (Array.isArray(cursor)) {\n const field = arrayKeyField(cursor)\n cursor = field\n ? cursor.find(\n (entry) => (entry as Record<string, unknown>)[field] === segment,\n )\n : undefined\n continue\n }\n if (!isPlainObject(cursor)) return undefined\n cursor = cursor[segment]\n }\n return cursor\n}\n"],"names":["stableStringify","OVERRIDE_DELETE","ARTIFACT_OVERRIDE_FIELD","KEY_FIELDS","isPlainObject","value","Array","isArray","Object","getPrototypeOf","prototype","same","a","b","isDeleteSentinel","isKeyedArrayPatch","arrayKeyField","length","every","field","keys","map","entry","key","Set","size","keyedArrayPatch","keyedBy","entries","order","undefined","resolveOverride","base","patch","resolve","resolveKeyedArray","merged","resolved","source","keyOf","push","present","has","added","rank","Map","index","get","sort","diffOverride","edited","child","keyed","diffKeyedArray","String","baseByKey","editedKeys","includes","natural","filter","reordered","overridePaths","prefix","flatMap","isEmptyOverride","values","isStorablePatch","canOverrideArtifact","provenance","state","ok","reason","updatable","overrideWriteValue","baseSha256","meta","updatedAt","updatedBy","readArtifactOverride","doc","stored","sha","resolveArtifactContent","content","override","overrideConflicts","incoming","overridden","path","baseValue","readPath","incomingValue","cursor","segment","match","find"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/marketplace-provenance.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 * Everything an org can publish to the marketplace (AGL-654).\n *\n * The union lives in core rather than the marketplace plugin because the\n * console reasons about installed artifacts too — a Plugins index that cannot\n * name an artifact type cannot render an update chip — and module boundaries\n * (rightly) forbid an app depending on an addon lib. The marketplace model\n * re-exports it, so publishing code still reads it from one place.\n */\nexport type MarketplaceArtifactType =\n | 'component'\n | 'template'\n | 'plugin'\n | 'layout'\n | 'datasetSchema'\n | 'emailTemplate'\n /**\n * A campaign email design, installed as a new `kind: 'email'` screen.\n *\n * Distinct from `emailTemplate`, which is a design FOR one entry in the\n * fixed transactional catalog and can only ever land on that same key. A\n * starter belongs to no key: it is a blank-page replacement for the composer,\n * so it installs as a new document and the tenant may install ten of them.\n * Folding the two together would give one install button two behaviors and\n * would make the catalog key an absent-field discriminator.\n */\n | 'emailStarter'\n | 'theme'\n\n/**\n * Install provenance (AGL-1015): where an installed artifact came from, at\n * what version, and what it looked like when it arrived.\n *\n * Every install route used to stamp its own shape — plugins wrote a pin,\n * dataset schemas a `source`, email templates an `installedFrom`, components a\n * `marketplace` — and none of them kept the content as installed. Without the\n * original there is no way to tell the publisher's change from the user's, so\n * an update can only overwrite. This module defines the one shape all install\n * routes write and the one reader everything else asks.\n */\n\n/**\n * Immutable, content-addressed base snapshots — the content exactly as\n * installed, keyed by its own hash and stored outside the user's edit path.\n *\n * Top-level rather than per-host on purpose: the same listing version installed\n * onto twenty sites is one document, and re-installing it rewrites nothing.\n */\nexport const ARTIFACT_BASE_COLLECTION = 'marketplaceArtifactBases'\n\n/**\n * Firestore's per-document ceiling is 1 MiB. Snapshots are stored whole, so\n * anything near it is refused rather than truncated — a partial base is worse\n * than no base, because a diff would report the missing half as user edits.\n */\nexport const ARTIFACT_BASE_MAX_BYTES = 800 * 1024\n\n/** The provenance stamp written on every installed artifact (AGL-1015). */\nexport interface InstalledFrom {\n listingId: string\n /** The listing version installed, as a string; null when unversioned. */\n version: string | null\n /** Content hash of the base snapshot — the key into {@link ARTIFACT_BASE_COLLECTION}. */\n sha256: string | null\n artifactType: MarketplaceArtifactType\n installedAt: unknown\n /** The publishing org (`listing.profileId`, org-owned since AGL-652). */\n publisherOrgId: string | null\n /**\n * What review said about THE VERSION INSTALLED, recorded at install time.\n *\n * Optional because six of the seven artifact types are auto-listed and have\n * no verdict to record. Where it is written it is a fact about one version\n * and is never recomputed from the listing afterwards: a listing that earns\n * `reviewStatus: 'verified'` next month did not retroactively review the\n * bytes installed today, and a surface that resolved assurance live would\n * say it did. Stamping it is what keeps \"unreviewed\" from decaying into a\n * silence a reader mistakes for approval.\n */\n assurance?: 'approved' | 'unreviewed' | 'rejected'\n}\n\n/**\n * How much is actually known about where an artifact came from.\n *\n * `inferred` exists because the marketplace shipped before this stamp did:\n * pre-AGL-1015 installs carry a listing id somewhere but no hash and no base.\n * Calling those `recorded` would let an update think it has an origin to merge\n * against; calling them `unknown` would throw away a listing id we really do\n * have. They are their own state, and the UI says \"installed before update\n * tracking\" rather than implying either.\n */\nexport type ProvenanceState = 'recorded' | 'inferred' | 'unknown'\n\nexport interface ResolvedProvenance {\n state: ProvenanceState\n listingId: string | null\n version: string | null\n sha256: string | null\n artifactType: MarketplaceArtifactType | null\n publisherOrgId: string | null\n /**\n * A base snapshot exists, so this artifact can be diffed and safely updated.\n * False for everything installed before the stamp — and saying so plainly is\n * the point: a merge built on a fabricated origin destroys work silently.\n */\n updatable: boolean\n}\n\nconst UNKNOWN_PROVENANCE: ResolvedProvenance = {\n state: 'unknown',\n listingId: null,\n version: null,\n sha256: null,\n artifactType: null,\n publisherOrgId: null,\n updatable: false,\n}\n\nfunction asVersion(value: unknown): string | null {\n return value == null || value === '' ? null : String(value)\n}\n\n/**\n * Reads provenance off an installed document, tolerating every shape the\n * install routes wrote before AGL-1015.\n *\n * The legacy fields are read rather than migrated: a batch backfill could only\n * ever recover the listing id and version, which is exactly what this recovers\n * lazily, and it cannot invent a base snapshot. Installs re-stamp themselves\n * properly the next time they are updated.\n */\nexport function resolveProvenance(\n doc:\n | {\n installedFrom?: Partial<InstalledFrom> | null\n /** Component installs (`hosts/{h}/components`). */\n marketplace?: { listingId?: string; profileId?: string; version?: unknown } | null\n /** Template, layout and dataset-schema installs. */\n source?: { type?: string; listingId?: string; version?: unknown } | null\n /** Plugin pins are their own provenance: the pin IS the version. */\n pluginId?: string\n listingId?: string\n profileId?: string\n version?: unknown\n sha256?: string\n }\n | null\n | undefined,\n fallbackArtifactType?: MarketplaceArtifactType,\n): ResolvedProvenance {\n if (!doc) return UNKNOWN_PROVENANCE\n\n const stamped = doc.installedFrom\n // A full stamp is the only thing that carries a base snapshot. Email\n // templates have written a partial `installedFrom` since AGL-789, so the\n // discriminator is the hash and the artifact type, not the field's presence.\n if (stamped?.listingId && stamped.sha256 && stamped.artifactType) {\n return {\n state: 'recorded',\n listingId: stamped.listingId,\n version: asVersion(stamped.version),\n sha256: stamped.sha256,\n artifactType: stamped.artifactType as MarketplaceArtifactType,\n publisherOrgId: stamped.publisherOrgId ?? null,\n updatable: true,\n }\n }\n\n const legacy = stamped?.listingId\n ? { listingId: stamped.listingId, version: stamped.version, profileId: null }\n : doc.marketplace?.listingId\n ? {\n listingId: doc.marketplace.listingId,\n version: doc.marketplace.version,\n profileId: doc.marketplace.profileId ?? null,\n }\n : doc.source?.listingId\n ? {\n listingId: doc.source.listingId,\n version: doc.source.version,\n profileId: null,\n }\n : // A plugin pin is flat: `{listingId, version, sha256}` on the install\n // doc itself. It is the one artifact that never needed a snapshot —\n // the bytes are immutable and content-addressed already — so a pin\n // resolves as fully recorded.\n doc.listingId\n ? {\n listingId: doc.listingId,\n version: doc.version,\n profileId: doc.profileId ?? null,\n }\n : null\n if (!legacy) return UNKNOWN_PROVENANCE\n\n const isPin = Boolean(doc.pluginId && doc.sha256)\n return {\n state: isPin ? 'recorded' : 'inferred',\n listingId: legacy.listingId,\n version: asVersion(legacy.version),\n sha256: doc.sha256 ?? null,\n artifactType: isPin ? 'plugin' : (fallbackArtifactType ?? null),\n publisherOrgId: legacy.profileId,\n updatable: isPin,\n }\n}\n\n/**\n * Deterministic JSON with object keys in sorted order, so the same content\n * hashes to the same id no matter what order Firestore handed the fields back.\n *\n * `undefined` members are dropped exactly as `JSON.stringify` drops them, which\n * keeps `{a: 1}` and `{a: 1, b: undefined}` — the same document after a\n * Firestore round-trip — hashing alike.\n */\nexport function stableStringify(value: unknown): string {\n if (value === null || typeof value !== 'object') {\n return JSON.stringify(value ?? null)\n }\n if (Array.isArray(value)) {\n return `[${value.map((entry) => stableStringify(entry)).join(',')}]`\n }\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, entryValue]) => entryValue !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`)\n return `{${entries.join(',')}}`\n}\n"],"names":["ARTIFACT_BASE_COLLECTION","ARTIFACT_BASE_MAX_BYTES","UNKNOWN_PROVENANCE","state","listingId","version","sha256","artifactType","publisherOrgId","updatable","asVersion","value","String","resolveProvenance","doc","fallbackArtifactType","stamped","installedFrom","legacy","profileId","marketplace","source","isPin","Boolean","pluginId","stableStringify","JSON","stringify","Array","isArray","map","entry","join","entries","Object","filter","entryValue","undefined","sort","a","b","key"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;CAQC,GAqBD;;;;;;;;;;CAUC,GAED;;;;;;CAMC,GACD,OAAO,MAAMA,2BAA2B,2BAA0B;AAElE;;;;CAIC,GACD,OAAO,MAAMC,0BAA0B,MAAM,KAAI;AAsDjD,MAAMC,qBAAyC;IAC7CC,OAAO;IACPC,WAAW;IACXC,SAAS;IACTC,QAAQ;IACRC,cAAc;IACdC,gBAAgB;IAChBC,WAAW;AACb;AAEA,SAASC,UAAUC,KAAc;IAC/B,OAAOA,SAAS,QAAQA,UAAU,KAAK,OAAOC,OAAOD;AACvD;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASE,kBACdC,GAea,EACbC,oBAA8C;QA0B3BD,4BAgBIA,gBAUbA;QA9BNA,kBAMEA;IA1BN,IAAI,CAACA,KAAK,OAAOZ;IAEjB,MAAMc,UAAUF,IAAIG,aAAa;IACjC,qEAAqE;IACrE,yEAAyE;IACzE,6EAA6E;IAC7E,IAAID,CAAAA,2BAAAA,QAASZ,SAAS,KAAIY,QAAQV,MAAM,IAAIU,QAAQT,YAAY,EAAE;YAO9CS;QANlB,OAAO;YACLb,OAAO;YACPC,WAAWY,QAAQZ,SAAS;YAC5BC,SAASK,UAAUM,QAAQX,OAAO;YAClCC,QAAQU,QAAQV,MAAM;YACtBC,cAAcS,QAAQT,YAAY;YAClCC,cAAc,GAAEQ,0BAAAA,QAAQR,cAAc,YAAtBQ,0BAA0B;YAC1CP,WAAW;QACb;IACF;IAEA,MAAMS,SAASF,CAAAA,2BAAAA,QAASZ,SAAS,IAC7B;QAAEA,WAAWY,QAAQZ,SAAS;QAAEC,SAASW,QAAQX,OAAO;QAAEc,WAAW;IAAK,IAC1EL,EAAAA,mBAAAA,IAAIM,WAAW,qBAAfN,iBAAiBV,SAAS,IACxB;QACEA,WAAWU,IAAIM,WAAW,CAAChB,SAAS;QACpCC,SAASS,IAAIM,WAAW,CAACf,OAAO;QAChCc,SAAS,GAAEL,6BAAAA,IAAIM,WAAW,CAACD,SAAS,YAAzBL,6BAA6B;IAC1C,IACAA,EAAAA,cAAAA,IAAIO,MAAM,qBAAVP,YAAYV,SAAS,IACnB;QACEA,WAAWU,IAAIO,MAAM,CAACjB,SAAS;QAC/BC,SAASS,IAAIO,MAAM,CAAChB,OAAO;QAC3Bc,WAAW;IACb,IAEA,oEAAoE;IACpE,mEAAmE;IACnE,8BAA8B;IAC9BL,IAAIV,SAAS,GACX;QACEA,WAAWU,IAAIV,SAAS;QACxBC,SAASS,IAAIT,OAAO;QACpBc,SAAS,GAAEL,iBAAAA,IAAIK,SAAS,YAAbL,iBAAiB;IAC9B,IACA;IACV,IAAI,CAACI,QAAQ,OAAOhB;IAEpB,MAAMoB,QAAQC,QAAQT,IAAIU,QAAQ,IAAIV,IAAIR,MAAM;IAChD,OAAO;QACLH,OAAOmB,QAAQ,aAAa;QAC5BlB,WAAWc,OAAOd,SAAS;QAC3BC,SAASK,UAAUQ,OAAOb,OAAO;QACjCC,MAAM,GAAEQ,cAAAA,IAAIR,MAAM,YAAVQ,cAAc;QACtBP,cAAce,QAAQ,WAAYP,+BAAAA,uBAAwB;QAC1DP,gBAAgBU,OAAOC,SAAS;QAChCV,WAAWa;IACb;AACF;AAEA;;;;;;;CAOC,GACD,OAAO,SAASG,gBAAgBd,KAAc;IAC5C,IAAIA,UAAU,QAAQ,OAAOA,UAAU,UAAU;QAC/C,OAAOe,KAAKC,SAAS,CAAChB,gBAAAA,QAAS;IACjC;IACA,IAAIiB,MAAMC,OAAO,CAAClB,QAAQ;QACxB,OAAO,CAAC,CAAC,EAAEA,MAAMmB,GAAG,CAAC,CAACC,QAAUN,gBAAgBM,QAAQC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtE;IACA,MAAMC,UAAUC,OAAOD,OAAO,CAACtB,OAC5BwB,MAAM,CAAC,CAAC,GAAGC,WAAW,GAAKA,eAAeC,WAC1CC,IAAI,CAAC,CAAC,CAACC,EAAE,EAAE,CAACC,EAAE,GAAMD,IAAIC,IAAI,CAAC,IAAID,IAAIC,IAAI,IAAI,GAC7CV,GAAG,CAAC,CAAC,CAACW,KAAKL,WAAW,GAAK,GAAGV,KAAKC,SAAS,CAACc,KAAK,CAAC,EAAEhB,gBAAgBW,aAAa;IACrF,OAAO,CAAC,CAAC,EAAEH,QAAQD,IAAI,CAAC,KAAK,CAAC,CAAC;AACjC"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/marketplace-theme.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 {\n HostTheme,\n HostThemeFont,\n HostThemeScheme,\n HostThemeSchemeColors,\n} from '@aglyn/shared-data-types'\nimport {\n diffOverride,\n isEmptyOverride,\n overrideConflicts,\n overridePaths,\n readArtifactOverride,\n resolveOverride,\n type ArtifactOverride,\n} from './marketplace-overrides'\n\n/**\n * Publish-time validation for themes as a marketplace artifact (AGL-1020).\n *\n * A plugin bundle is validated because it is code and the risk is a\n * compromised site. A theme is data, so the risk is different in kind but not\n * in consequence: the failure mode is an unreadable or half-painted site, which\n * a publisher cannot see from their own machine because their fonts are\n * installed and their eyes already know what the text says.\n *\n * Both of the things that go wrong are mechanically checkable, so they are\n * checked here rather than left to review:\n *\n * * **Completeness.** Light and dark are not one palette with a filter. A theme\n * that defines only light is half a theme, and installing it leaves every\n * dark-mode visitor on MUI's stock blue — which reads as the site being\n * broken, not as the theme being partial.\n * * **Contrast.** Text on its background, and label on its button, are the two\n * pairs that make a site unusable when they are wrong. WCAG gives an exact\n * ratio, so \"is this readable\" has an answer rather than an opinion.\n *\n * Errors refuse the publish; warnings are shown and can be published past. The\n * split is deliberate — a 3.9:1 heading is a judgement call a designer may\n * legitimately win, but a theme with no dark scheme is not a complete artifact\n * whoever is asking.\n *\n * Pure and MUI-free so the publish route, the console's pre-flight and the\n * tests all reach the same verdict from the same code.\n */\n\nexport interface ThemeValidationIssue {\n /** Dotted path into the theme, so the editor can focus the offending field. */\n path: string\n message: string\n}\n\nexport interface ThemeValidation {\n /** No errors. Warnings do not block. */\n ok: boolean\n errors: ThemeValidationIssue[]\n warnings: ThemeValidationIssue[]\n}\n\n/**\n * WCAG AA for body text. The same 4.5 the accessibility audit uses, rather\n * than a house number, so a theme that passes here passes there.\n */\nexport const CONTRAST_AA = 4.5\n\n/**\n * WCAG AA for large text (18pt+/14pt bold) and UI component boundaries. Button\n * labels are judged against this rather than 4.5 because they are large, short\n * and centred, and holding them to body-text contrast rejects palettes that are\n * genuinely fine.\n */\nexport const CONTRAST_AA_LARGE = 3\n\nconst SCHEMES: HostThemeScheme[] = ['light', 'dark']\n\n/**\n * Font families that need no loading. Anything else is either declared in the\n * theme's own `fonts` array or will silently fall back to the browser's default\n * — which is the failure the issue calls out, so it is at least a warning.\n */\nconst SYSTEM_FAMILIES = new Set(\n [\n 'system-ui',\n 'sans-serif',\n 'serif',\n 'monospace',\n 'cursive',\n 'fantasy',\n 'ui-sans-serif',\n 'ui-serif',\n 'ui-monospace',\n 'ui-rounded',\n '-apple-system',\n 'blinkmacsystemfont',\n 'segoe ui',\n 'roboto',\n 'helvetica',\n 'helvetica neue',\n 'arial',\n 'georgia',\n 'times new roman',\n 'courier new',\n 'inherit',\n 'initial',\n ].map((family) => family.toLowerCase()),\n)\n\n/**\n * Parses a CSS colour into sRGB channels, or null when it is not something we\n * can reason about.\n *\n * `null` means \"not checked\", never \"failed\": a theme is free to use `color-mix`\n * or a CSS variable, and refusing to publish because this parser is narrower\n * than CSS would be a validator inventing a rule the platform does not have.\n */\nexport function parseColor(\n value: unknown,\n): { r: number; g: number; b: number } | null {\n if (typeof value !== 'string') return null\n const text = value.trim().toLowerCase()\n\n const hex = /^#([0-9a-f]{3,8})$/.exec(text)\n if (hex) {\n const digits = hex[1]\n if (digits.length === 3 || digits.length === 4) {\n return {\n r: parseInt(digits[0] + digits[0], 16),\n g: parseInt(digits[1] + digits[1], 16),\n b: parseInt(digits[2] + digits[2], 16),\n }\n }\n if (digits.length === 6 || digits.length === 8) {\n return {\n r: parseInt(digits.slice(0, 2), 16),\n g: parseInt(digits.slice(2, 4), 16),\n b: parseInt(digits.slice(4, 6), 16),\n }\n }\n return null\n }\n\n const rgb = /^rgba?\\(([^)]+)\\)$/.exec(text)\n if (rgb) {\n const parts = rgb[1].split(/[,/\\s]+/).filter(Boolean)\n if (parts.length < 3) return null\n const channels = parts.slice(0, 3).map((part) =>\n part.endsWith('%')\n ? (Number(part.slice(0, -1)) / 100) * 255\n : Number(part),\n )\n if (channels.some((channel) => !Number.isFinite(channel))) return null\n const [r, g, b] = channels\n return { r, g, b }\n }\n\n if (text === 'white') return { r: 255, g: 255, b: 255 }\n if (text === 'black') return { r: 0, g: 0, b: 0 }\n return null\n}\n\n/** WCAG relative luminance. */\nfunction luminance(channels: { r: number; g: number; b: number }): number {\n const channel = (raw: number) => {\n const value = Math.min(255, Math.max(0, raw)) / 255\n return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4\n }\n return (\n 0.2126 * channel(channels.r) +\n 0.7152 * channel(channels.g) +\n 0.0722 * channel(channels.b)\n )\n}\n\n/**\n * WCAG contrast ratio between two colours, or null when either is unparseable.\n * Ranges 1 (identical) to 21 (black on white).\n */\nexport function contrastRatio(a: unknown, b: unknown): number | null {\n const first = parseColor(a)\n const second = parseColor(b)\n if (!first || !second) return null\n const light = Math.max(luminance(first), luminance(second))\n const dark = Math.min(luminance(first), luminance(second))\n return (light + 0.05) / (dark + 0.05)\n}\n\nconst round = (ratio: number) => Math.round(ratio * 10) / 10\n\n/**\n * The colours a scheme must actually define to be usable on its own.\n *\n * Deliberately short. MUI derives an enormous amount from very little, and\n * demanding a full palette would reject good themes; but a scheme that names no\n * primary, no background and no text has not expressed an identity at all — it\n * is an empty object that happens to have a key.\n */\nfunction validateScheme(\n scheme: HostThemeScheme,\n colors: HostThemeSchemeColors | undefined,\n errors: ThemeValidationIssue[],\n warnings: ThemeValidationIssue[],\n): void {\n const at = `colorSchemes.${scheme}`\n if (!colors || !Object.keys(colors).length) {\n errors.push({\n path: at,\n message:\n `This theme defines no ${scheme} scheme. Light and dark are not one ` +\n 'palette with a filter — install it and every visitor in ' +\n `${scheme} mode sees the stock palette instead of your design.`,\n })\n return\n }\n if (!colors.primary?.main) {\n errors.push({\n path: `${at}.primary.main`,\n message: `The ${scheme} scheme has no primary color.`,\n })\n }\n\n const background = colors.background?.default\n const paper = colors.background?.paper\n const text = colors.text?.primary\n const secondaryText = colors.text?.secondary\n\n // Text on background is the pair that decides whether a site can be read at\n // all, so it is the one contrast failure that refuses the publish.\n const bodyContrast = contrastRatio(text, background)\n if (bodyContrast != null && bodyContrast < CONTRAST_AA) {\n errors.push({\n path: `${at}.text.primary`,\n message:\n `Body text on the ${scheme} background is ${round(bodyContrast)}:1, ` +\n `below the ${CONTRAST_AA}:1 minimum for readable text.`,\n })\n }\n\n const paperContrast = contrastRatio(text, paper)\n if (paperContrast != null && paperContrast < CONTRAST_AA) {\n errors.push({\n path: `${at}.background.paper`,\n message:\n `Body text on ${scheme} cards and menus is ${round(paperContrast)}:1, ` +\n `below the ${CONTRAST_AA}:1 minimum. Surfaces inherit this everywhere ` +\n 'a Paper is used, which is most of the site.',\n })\n }\n\n // Secondary text is legitimately dimmer by design, so it warns rather than\n // refuses — but silently shipping unreadable captions is worse than saying so.\n const captionContrast = contrastRatio(secondaryText, background)\n if (captionContrast != null && captionContrast < CONTRAST_AA) {\n warnings.push({\n path: `${at}.text.secondary`,\n message:\n `Secondary text on the ${scheme} background is ` +\n `${round(captionContrast)}:1. Captions and helper text will be hard ` +\n 'to read.',\n })\n }\n\n // A button whose label disappears into its own fill. Only checkable when the\n // theme states contrastText — otherwise MUI derives one, and derives it well.\n for (const key of ['primary', 'secondary', 'error'] as const) {\n const color = colors[key]\n const ratio = contrastRatio(color?.contrastText, color?.main)\n if (ratio != null && ratio < CONTRAST_AA_LARGE) {\n warnings.push({\n path: `${at}.${key}.contrastText`,\n message:\n `The ${scheme} ${key} button label is ${round(ratio)}:1 against its ` +\n `own fill, below ${CONTRAST_AA_LARGE}:1. Leave contrast text unset ` +\n 'to let the theme derive a readable one.',\n })\n }\n }\n}\n\n/**\n * Every family named in the typography, split out of its CSS stack.\n *\n * A stack is a fallback chain, so only the FIRST entry is the one the design\n * depends on; the rest exist precisely to be substituted and warning about them\n * would be noise.\n */\nfunction declaredFamilies(theme: HostTheme): string[] {\n const stacks: string[] = []\n if (theme.typography?.fontFamily) stacks.push(theme.typography.fontFamily)\n for (const variant of Object.values(theme.typography?.variants ?? {})) {\n if (variant?.fontFamily) stacks.push(variant.fontFamily)\n }\n const primary: string[] = []\n for (const stack of stacks) {\n const first = stack.split(',')[0]?.trim().replace(/^[\"']|[\"']$/g, '')\n if (first) primary.push(first)\n }\n return primary\n}\n\nfunction validateFonts(\n theme: HostTheme,\n warnings: ThemeValidationIssue[],\n): void {\n const loadable = new Set(\n (theme.fonts ?? [])\n .map((font: HostThemeFont) => font.family?.trim().toLowerCase())\n .filter(Boolean) as string[],\n )\n const seen = new Set<string>()\n for (const family of declaredFamilies(theme)) {\n const key = family.toLowerCase()\n if (seen.has(key) || loadable.has(key) || SYSTEM_FAMILIES.has(key)) continue\n seen.add(key)\n warnings.push({\n path: 'typography.fontFamily',\n message:\n `\"${family}\" is used but not listed in the theme's fonts, so it will ` +\n 'only appear on machines that happen to have it installed. Add it as ' +\n 'a Google font, or fall back to a system family.',\n })\n }\n}\n\n/**\n * Validates a theme for publishing.\n *\n * The same function backs the console's pre-flight and the publish route: a\n * publisher who is told \"this will be refused\" and then has it accepted (or the\n * reverse) stops trusting either, so there is one verdict from one place.\n */\nexport function validateThemeForPublish(\n theme: HostTheme | null | undefined,\n): ThemeValidation {\n const errors: ThemeValidationIssue[] = []\n const warnings: ThemeValidationIssue[] = []\n\n if (!theme || !Object.keys(theme).length) {\n return {\n ok: false,\n errors: [\n {\n path: '',\n message:\n 'This site has no theme customisations yet, so there is nothing ' +\n 'to publish.',\n },\n ],\n warnings,\n }\n }\n\n for (const scheme of SCHEMES) {\n validateScheme(scheme, theme.colorSchemes?.[scheme], errors, warnings)\n }\n validateFonts(theme, warnings)\n\n return { ok: !errors.length, errors, warnings }\n}\n\n/**\n * Every field of a `HostTheme` that is part of the published artifact.\n *\n * Typed as a total record over `keyof HostTheme` on purpose: adding a field to\n * `HostTheme` fails to compile here until someone decides whether it ships. The\n * alternative — a hand-written list of `if` statements — silently drops the new\n * field, and a theme that loses one is not obviously broken, it is subtly\n * wrong. `mixins` arrived exactly that way (AGL-1242, toolbar heights) while\n * this was being written, which is why the check is a type and not a comment.\n *\n * A `false` means \"host-local, never published\". `darkScheme` is the one such\n * field: whether visitors get a dark scheme is a decision about a SITE's\n * content (does it read in dark?), not about the design being published, so a\n * publisher's opt-out must not travel with the theme and an installer's must\n * not read as a divergence from it.\n */\nconst THEME_ARTIFACT_FIELDS: Record<keyof Required<HostTheme>, boolean> = {\n colorSchemes: true,\n typography: true,\n fonts: true,\n shape: true,\n spacing: true,\n mixins: true,\n components: true,\n darkScheme: false,\n}\n\n/**\n * The theme content as published — the artifact and nothing else.\n *\n * What goes in here is what \"reset to the publisher's version\" restores and\n * what the update diff compares against, so it must hold no per-install data:\n * a host document also carries `updatedAt`, `updatedBy` and whatever else the\n * editor writes alongside, and any of those in the content would read as a user\n * edit on the next diff.\n *\n * Empty branches are dropped so two themes that render identically also hash\n * identically — `{fonts: []}` and `{}` must not produce two base snapshots.\n */\nexport function themeArtifactContent(\n theme: HostTheme | null | undefined,\n): HostTheme {\n const content: Record<string, unknown> = {}\n if (!theme) return content\n for (const [key, ships] of Object.entries(THEME_ARTIFACT_FIELDS)) {\n if (!ships) continue\n const value = (theme as Record<string, unknown>)[key]\n if (value == null) continue\n if (Array.isArray(value) ? !value.length : typeof value === 'object'\n ? !Object.keys(value as object).length\n : false) {\n continue\n }\n content[key] = value\n }\n return content\n}\n\n/**\n * A short, human summary of what a theme actually contains, for the install\n * confirmation and the listing card.\n *\n * Installing a theme replaces a site's visual identity, so the confirmation has\n * to say what is about to change in terms someone recognises — \"both schemes,\n * 2 fonts, 4 component styles\" — rather than showing them a JSON blob and\n * asking them to agree to it.\n */\nexport function describeTheme(theme: HostTheme | null | undefined): string[] {\n if (!theme) return []\n const parts: string[] = []\n const schemes = SCHEMES.filter(\n (scheme) => Object.keys(theme.colorSchemes?.[scheme] ?? {}).length,\n )\n if (schemes.length === 2) parts.push('Light and dark schemes')\n else if (schemes.length === 1) parts.push(`${schemes[0]} scheme only`)\n\n const fonts = theme.fonts?.length ?? 0\n if (fonts) parts.push(`${fonts} font${fonts === 1 ? '' : 's'}`)\n if (theme.typography?.variants) {\n const count = Object.keys(theme.typography.variants).length\n if (count) parts.push(`${count} text style${count === 1 ? '' : 's'}`)\n }\n if (theme.shape?.borderRadius != null) {\n parts.push(`${theme.shape.borderRadius}px corners`)\n }\n if (typeof theme.spacing === 'number') parts.push(`${theme.spacing}px spacing`)\n const components = Object.keys(theme.components ?? {}).length\n if (components) {\n parts.push(`${components} component style${components === 1 ? '' : 's'}`)\n }\n return parts\n}\n\n/* ------------------------------------------------------------------------ *\n * The override layer, applied to themes (AGL-1021)\n * ------------------------------------------------------------------------ */\n\n/**\n * The field on a host document holding the site's theme override.\n *\n * A theme is a FIELD on the host, not a document of its own, so it cannot use\n * `ARTIFACT_OVERRIDE_FIELD` — a host may one day override more than one thing,\n * and a bare `overrides` would be the first to claim the name. The override\n * layer is general precisely so a namespaced field works with it unchanged.\n */\nexport const THEME_OVERRIDE_FIELD = 'themeOverride'\n\n/** A host document, as far as the theme layer is concerned. */\nexport interface ThemeHostDocument {\n theme?: HostTheme | null\n themeOverride?: unknown\n themeInstalledFrom?: { sha256?: string | null; listingId?: string } | null\n /** The library theme the site picked (AGL-3404) — see `theme-library`. */\n themeSelection?: unknown\n}\n\n/** Reads the stored theme override off a host document, tolerating junk. */\nexport function readThemeOverride(\n host: ThemeHostDocument | null | undefined,\n): ArtifactOverride | undefined {\n // The override layer's reader is keyed to its own field name, so hand it a\n // shim rather than duplicating its validation — which is where the \"a\n // top-level null blanks the artifact\" guard lives.\n return readArtifactOverride({ overrides: host?.themeOverride })\n}\n\n/**\n * The site's effective theme: the marketplace theme (or the site's own) with\n * this site's overrides resolved over it.\n *\n * This is the MIDDLE and TOP of the three layers the issue describes. The\n * bottom — the platform default — is applied further down by\n * `HostThemeProvider`, which layers whatever this returns onto the brand base\n * (AGL-1180) so a theme that omits a value still yields a complete theme\n * rather than an undefined-shaped hole. Doing it there rather than here is not\n * a detail: the default is a runtime MUI theme with FUNCTION style overrides\n * that JSON cannot represent, so a \"default\" merged at this layer would be a\n * lossy copy of the real one.\n *\n * Every surface that renders a site's appearance calls this — the tenant, all\n * four besigner editors, and the document preview — because a theme that\n * resolves differently in the editor than on the site is worse than one that\n * does not resolve at all.\n */\nexport function resolveSiteTheme(\n host: ThemeHostDocument | null | undefined,\n): HostTheme | undefined {\n const base = host?.theme ?? undefined\n const override = readThemeOverride(host)\n if (!override) return base\n return resolveOverride<HostTheme>(base ?? {}, override.patch)\n}\n\n/**\n * Where a site's theme came from (AGL-2938).\n *\n * - `installed` — a marketplace theme. The site's edits are its override\n * patch over the publisher's version, which stays untouched and can still\n * take an update.\n * - `preset` — a built-in theme a plugin contributes (AGL-3404).\n * - `custom` — one of the site's own themes.\n * - `default` — the platform default.\n *\n * Whichever it is, an edit is an override patch over the picked theme and the\n * theme itself is never rewritten (AGL-3404), so every source can be restored.\n *\n * One reading for every surface that needs it — the page that lays out the\n * Theme section, a widget proposing a change there, and a generator deciding\n * what its change will be stored as — so none of them can file a site under\n * a different source than the save path does.\n */\nexport type HostThemeSource = 'installed' | 'preset' | 'custom' | 'default'\n\nconst THEME_SOURCES: readonly HostThemeSource[] = [\n 'installed',\n 'preset',\n 'custom',\n 'default',\n]\n\nexport function hostThemeSource(\n host: ThemeHostDocument | null | undefined,\n): HostThemeSource {\n const picked = (host?.themeSelection as { kind?: unknown } | null | undefined)\n ?.kind\n if (THEME_SOURCES.includes(picked as HostThemeSource)) {\n return picked as HostThemeSource\n }\n if (host?.themeInstalledFrom?.listingId) return 'installed'\n return host?.theme && Object.keys(host.theme).length ? 'custom' : 'default'\n}\n\n/**\n * Was this override authored against the theme currently installed?\n *\n * False after a theme swap, which is the whole reason `baseSha256` is recorded.\n * Overrides deliberately SURVIVE a swap (the issue's call, and the right one —\n * a brand colour still expresses intent when the theme underneath it changes)\n * but surviving silently is what would surprise people, so every surface that\n * shows an override can ask this and say so.\n *\n * True when there is no override, and when either side has no hash to compare:\n * \"we cannot tell\" must not render as \"these are stale\".\n */\nexport function isOverrideForCurrentTheme(\n host: ThemeHostDocument | null | undefined,\n): boolean {\n const override = readThemeOverride(host)\n if (!override?.baseSha256) return true\n const installed = host?.themeInstalledFrom?.sha256\n if (!installed) return true\n return override.baseSha256 === installed\n}\n\n/** One row of the \"what have I changed?\" view. */\nexport interface ThemeOverrideEntry {\n /** Dotted path into the theme, e.g. `colorSchemes.dark.primary.main`. */\n path: string\n /** A human label for the path, for a UI that is not a JSON viewer. */\n label: string\n /** Which scheme this touches, when it touches one. */\n scheme?: HostThemeScheme\n /** The publisher's value, or undefined when the override adds the field. */\n themeValue: unknown\n /** What this site set it to, or undefined when the override removes it. */\n overrideValue: unknown\n}\n\nconst SEGMENT_LABELS: Record<string, string> = {\n darkScheme: 'Dark scheme',\n colorSchemes: 'Color',\n typography: 'Typography',\n components: 'Component',\n shape: 'Shape',\n spacing: 'Spacing',\n fonts: 'Fonts',\n mixins: 'Layout',\n variants: '',\n background: 'background',\n text: 'text',\n main: '',\n contrastText: 'label color',\n borderRadius: 'corner radius',\n fontFamily: 'font',\n}\n\n/**\n * A readable label for a theme path.\n *\n * \"Colour · dark · primary\" beats `colorSchemes.dark.primary.main`, and the\n * difference matters because this list is the answer to \"what have I changed?\"\n * — a question nobody asks wanting to read JSON paths.\n */\nexport function describeThemePath(path: string): string {\n const segments = path.split('.').filter(Boolean)\n const words: string[] = []\n for (const segment of segments) {\n const mapped = SEGMENT_LABELS[segment]\n if (mapped === '') continue\n words.push(mapped ?? segment)\n }\n return words.join(' · ') || 'the whole theme'\n}\n\n/**\n * \"What have I changed?\" — the patch, rendered.\n *\n * Literally a read of the stored override rather than a diff computed at call\n * time, which is the property the whole layer exists to provide: the answer is\n * the thing that was saved, so it cannot disagree with what will be applied.\n */\nexport function describeThemeOverride(\n host: ThemeHostDocument | null | undefined,\n): ThemeOverrideEntry[] {\n const override = readThemeOverride(host)\n if (!override) return []\n const base = host?.theme ?? {}\n const resolved = resolveOverride<HostTheme>(base, override.patch)\n return overridePaths(override.patch).map((path) => {\n const segments = path.split('.')\n const scheme =\n segments[0] === 'colorSchemes' &&\n (segments[1] === 'light' || segments[1] === 'dark')\n ? (segments[1] as HostThemeScheme)\n : undefined\n return {\n path,\n label: describeThemePath(path),\n scheme,\n themeValue: readThemePath(base, path),\n overrideValue: readThemePath(resolved, path),\n }\n })\n}\n\n/** Reads a dotted path out of a theme. */\nfunction readThemePath(theme: unknown, path: string): unknown {\n let cursor: unknown = theme\n for (const segment of path.split('.')) {\n if (cursor == null || typeof cursor !== 'object') return undefined\n cursor = (cursor as Record<string, unknown>)[segment]\n }\n return cursor\n}\n\n/**\n * The override this site should store after the theme editor produced `edited`.\n *\n * The editor edits the RESOLVED theme — that is what it renders and what the\n * user sees — so the patch is the difference between the publisher's base and\n * what came back. Diffing here rather than tracking per-field dirty state means\n * a field the user set and then set back to the theme's value stops being an\n * override, which is what \"reset\" means without needing a reset button to have\n * been pressed.\n */\nexport function themeOverridePatch(\n host: ThemeHostDocument | null | undefined,\n edited: HostTheme,\n): unknown {\n return diffOverride(host?.theme ?? {}, edited)\n}\n\n/**\n * Where this site's overrides sit on top of values a theme update also changes\n * — the only place a conflict can exist.\n *\n * Narrow by construction: an override touching four paths can conflict in at\n * most four places however much of the theme the publisher rewrote. That is the\n * difference between \"the publisher also changed your heading colour, whose\n * wins?\" and \"this update rewrites your theme, continue?\".\n */\nexport function themeUpdateConflicts(\n host: ThemeHostDocument | null | undefined,\n incoming: HostTheme,\n): ThemeOverrideEntry[] {\n const override = readThemeOverride(host)\n if (!override || isEmptyOverride(override.patch)) return []\n const base = host?.theme ?? {}\n const resolved = resolveOverride<HostTheme>(base, override.patch)\n return overrideConflicts(base, override.patch, incoming).map((path) => ({\n path,\n label: describeThemePath(path),\n scheme:\n path.startsWith('colorSchemes.light.')\n ? ('light' as const)\n : path.startsWith('colorSchemes.dark.')\n ? ('dark' as const)\n : undefined,\n themeValue: readThemePath(incoming, path),\n overrideValue: readThemePath(resolved, path),\n }))\n}\n"],"names":["diffOverride","isEmptyOverride","overrideConflicts","overridePaths","readArtifactOverride","resolveOverride","CONTRAST_AA","CONTRAST_AA_LARGE","SCHEMES","SYSTEM_FAMILIES","Set","map","family","toLowerCase","parseColor","value","text","trim","hex","exec","digits","length","r","parseInt","g","b","slice","rgb","parts","split","filter","Boolean","channels","part","endsWith","Number","some","channel","isFinite","luminance","raw","Math","min","max","contrastRatio","a","first","second","light","dark","round","ratio","validateScheme","scheme","colors","errors","warnings","at","Object","keys","push","path","message","primary","main","background","default","paper","secondaryText","secondary","bodyContrast","paperContrast","captionContrast","key","color","contrastText","declaredFamilies","theme","stacks","typography","fontFamily","variant","values","variants","stack","replace","validateFonts","loadable","fonts","font","seen","has","add","validateThemeForPublish","ok","colorSchemes","THEME_ARTIFACT_FIELDS","shape","spacing","mixins","components","darkScheme","themeArtifactContent","content","ships","entries","Array","isArray","describeTheme","schemes","count","borderRadius","THEME_OVERRIDE_FIELD","readThemeOverride","host","overrides","themeOverride","resolveSiteTheme","base","undefined","override","patch","THEME_SOURCES","hostThemeSource","picked","themeSelection","kind","includes","themeInstalledFrom","listingId","isOverrideForCurrentTheme","baseSha256","installed","sha256","SEGMENT_LABELS","describeThemePath","segments","words","segment","mapped","join","describeThemeOverride","resolved","label","themeValue","readThemePath","overrideValue","cursor","themeOverridePatch","edited","themeUpdateConflicts","incoming","startsWith"],"mappings":"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"}
@@ -1,119 +0,0 @@
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 { MarketplaceArtifactType } from './marketplace-provenance';
18
- import { type PluginRevocation } from './plugin-manifest';
19
- /**
20
- * "Is what I have installed still what the publisher ships?" (AGL-1016).
21
- *
22
- * This comparison was previously derived inline on the installed-plugins card
23
- * and nowhere else, so the Plugins index, the installation page and the
24
- * marketplace listing could each show a different answer — or none. It is
25
- * exactly the sort of thing that gets re-derived slightly differently in four
26
- * places, so it lives here, pure, and every surface asks it.
27
- */
28
- export type UpdateState =
29
- /** Running the newest version the publisher offers. */
30
- 'current'
31
- /** A newer version exists and can be taken. */
32
- | 'update-available'
33
- /**
34
- * Installed version is NEWER than what is offered. Not a bug: a publisher
35
- * installs their own unapproved version to test it, and a version can be
36
- * withdrawn after someone installed it.
37
- */
38
- | 'ahead'
39
- /** Not enough is known to say — no provenance, or nothing published to compare. */
40
- | 'unknown';
41
- export interface UpdateStatus {
42
- state: UpdateState;
43
- /** The version running, or null when provenance cannot say. */
44
- installedVersion: string | null;
45
- /** The newest version on offer — for plugins, the newest APPROVED one. */
46
- availableVersion: string | null;
47
- /**
48
- * Why the answer is `unknown`, for a UI that should say which kind of
49
- * silence this is rather than implying the artifact is current.
50
- */
51
- unknownReason?: 'no-provenance' | 'nothing-published' | 'incomparable';
52
- }
53
- /**
54
- * Orders two artifact versions, or returns null when they cannot be ordered.
55
- *
56
- * Two numbering schemes are in play and both are real: copied artifacts get a
57
- * monotonic integer from the publish route, plugins get the semver string off
58
- * their manifest. A single string compare handles neither ("10" < "9",
59
- * "1.10.0" < "1.9.0"), so both are parsed as dotted numbers — which covers the
60
- * integer case as a one-segment version.
61
- *
62
- * Anything non-numeric (a prerelease tag, a date-stamped build) is not ordered
63
- * by guesswork: unequal-and-incomparable is reported as such and surfaces as
64
- * `unknown`, which is honest, where a string compare would confidently point
65
- * an update button at an older build.
66
- */
67
- export declare function compareArtifactVersions(a: string | number | null | undefined, b: string | number | null | undefined): -1 | 0 | 1 | null;
68
- /** The installed side: a plugin pin, or any copied artifact's document. */
69
- export interface UpdateComparableInstall {
70
- version?: unknown;
71
- installedFrom?: Record<string, unknown> | null;
72
- marketplace?: {
73
- listingId?: string;
74
- version?: unknown;
75
- } | null;
76
- source?: {
77
- listingId?: string;
78
- version?: unknown;
79
- } | null;
80
- listingId?: string;
81
- pluginId?: string;
82
- sha256?: string;
83
- }
84
- /** The listing side, as the public listing document carries it. */
85
- export interface UpdateComparableListing {
86
- artifactType?: string;
87
- type?: string;
88
- kind?: string;
89
- latestVersion?: unknown;
90
- /**
91
- * The newest version that PASSED REVIEW (AGL-966), denormalised onto the
92
- * listing by the approval route because `pluginVersions` is server-only.
93
- */
94
- latestApprovedVersion?: unknown;
95
- }
96
- /**
97
- * Compares what is installed against what the listing offers (AGL-1016).
98
- *
99
- * For plugins the offer is the newest INSTALLABLE version, never
100
- * `latestVersion`. Installs already resolve it that way; advertising
101
- * `latestVersion` would leak AGL-966's guarantee straight back out through a
102
- * badge, telling a workspace an update exists that the install route would
103
- * then refuse to give them.
104
- *
105
- * `revocation` is the same leak one state over (AGL-2368). Approval is a
106
- * review verdict and the kill switch does not clear it, so `latestApprovedVersion`
107
- * could name a stopped version — an "Update to vX" for bytes `install-plugin`
108
- * answers 409 to. Optional because it is a second document and not every
109
- * caller holds it; every writer of the mirror derives it from
110
- * `newestInstallableVersion` since AGL-2306, so a caller without one is
111
- * reading a cache that is correct except in the window between two writes.
112
- * A caller that HAS it should pass it.
113
- */
114
- export declare function resolveUpdateState(installed: UpdateComparableInstall | null | undefined, listing: UpdateComparableListing | null | undefined, artifactType?: MarketplaceArtifactType, revocation?: PluginRevocation | null): UpdateStatus;
115
- /**
116
- * One sentence describing an update state, so four surfaces do not each invent
117
- * their own wording for the same fact.
118
- */
119
- export declare function updateStateLabel(status: UpdateStatus): string;