@aceshooting/lyra-ui 23.0.0 → 24.1.0

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 (280) hide show
  1. package/CHANGELOG.md +16 -39
  2. package/README.md +72 -35
  3. package/custom-elements.json +1 -1
  4. package/design-tokens.json +1 -1
  5. package/dist/cli/lyra-rename-ledger.mjs +1 -1
  6. package/dist/cli/migration-contract.json +1 -1
  7. package/dist/cli/migration-contract.mjs +1 -1
  8. package/dist/cli/migration-renames.mjs +2 -2
  9. package/dist/cli/migration-transforms.mjs +2 -2
  10. package/dist/components/agent-tools/approval-queue/approval-queue.class.d.ts +1 -4
  11. package/dist/components/agent-tools/approval-queue/approval-queue.class.js +1 -3
  12. package/dist/components/agent-tools/compare-panel/compare-panel.class.d.ts +1 -4
  13. package/dist/components/agent-tools/compare-panel/compare-panel.class.js +1 -1
  14. package/dist/components/agent-tools/confirm-bar/confirm-bar.class.d.ts +1 -7
  15. package/dist/components/agent-tools/confirm-bar/confirm-bar.class.js +2 -2
  16. package/dist/components/agent-tools/context-inspector/context-inspector.class.d.ts +1 -1
  17. package/dist/components/agent-tools/evaluation-run/evaluation-run.class.d.ts +2 -5
  18. package/dist/components/agent-tools/evaluation-run/evaluation-run.class.js +1 -3
  19. package/dist/components/agent-tools/terminal/terminal.class.d.ts +1 -4
  20. package/dist/components/agent-tools/terminal/terminal.class.js +2 -2
  21. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.class.d.ts +1 -7
  22. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.class.js +1 -1
  23. package/dist/components/agent-tools/tool-timeline/tool-timeline.class.d.ts +1 -4
  24. package/dist/components/agent-tools/tool-timeline/tool-timeline.class.js +1 -3
  25. package/dist/components/charts/chart/lite-chart-axis-layout.d.ts +13 -0
  26. package/dist/components/charts/chart/lite-chart-axis-layout.js +1 -0
  27. package/dist/components/charts/chart/lite-chart.class.d.ts +1 -11
  28. package/dist/components/charts/chart/lite-chart.class.js +4 -4
  29. package/dist/components/conversation/agent-workspace/agent-workspace.class.d.ts +13 -3
  30. package/dist/components/conversation/agent-workspace/agent-workspace.class.js +1 -2
  31. package/dist/components/conversation/chat-message/chat-message.class.d.ts +3 -9
  32. package/dist/components/conversation/chat-message/chat-message.class.js +1 -1
  33. package/dist/components/conversation/code-block/code-block-base.class.js +1 -1
  34. package/dist/components/conversation/code-block/code-block-core.class.d.ts +6 -8
  35. package/dist/components/conversation/code-block/code-block-shared.d.ts +1 -4
  36. package/dist/components/conversation/code-block/code-block.class.d.ts +4 -6
  37. package/dist/components/conversation/markdown/markdown-base.class.d.ts +1 -3
  38. package/dist/components/conversation/markdown/markdown-base.class.js +1 -1
  39. package/dist/components/conversation/markdown/markdown-core.class.d.ts +1 -4
  40. package/dist/components/conversation/markdown/markdown.class.d.ts +1 -4
  41. package/dist/components/conversation/message-actions/message-actions.class.d.ts +1 -4
  42. package/dist/components/conversation/message-actions/message-actions.class.js +1 -1
  43. package/dist/components/conversation/message-feedback/message-feedback.class.d.ts +1 -4
  44. package/dist/components/conversation/message-feedback/message-feedback.class.js +2 -2
  45. package/dist/components/conversation/message-parts/message-parts.class.d.ts +2 -5
  46. package/dist/components/conversation/message-parts/message-parts.class.js +3 -3
  47. package/dist/components/conversation/streaming-text/streaming-text-base.class.d.ts +1 -1
  48. package/dist/components/conversation/streaming-text/streaming-text-core.class.d.ts +2 -0
  49. package/dist/components/conversation/streaming-text/streaming-text-core.class.js +0 -1
  50. package/dist/components/conversation/streaming-text/streaming-text.class.d.ts +2 -0
  51. package/dist/components/conversation/streaming-text/streaming-text.class.js +0 -1
  52. package/dist/components/conversation/thread-list/thread-list.class.d.ts +6 -17
  53. package/dist/components/conversation/thread-list/thread-list.class.js +6 -6
  54. package/dist/components/data/context-meter/context-meter.class.d.ts +1 -6
  55. package/dist/components/data/context-meter/context-meter.class.js +1 -1
  56. package/dist/components/data/data-grid/data-grid.class.d.ts +0 -1
  57. package/dist/components/data/data-grid/data-grid.class.js +2 -2
  58. package/dist/components/data/document-library/document-library.class.d.ts +6 -13
  59. package/dist/components/data/document-library/document-library.class.js +1 -2
  60. package/dist/components/data/heatmap/heatmap-grid-navigation.d.ts +7 -0
  61. package/dist/components/data/heatmap/heatmap-grid-navigation.js +1 -0
  62. package/dist/components/data/heatmap/heatmap.class.d.ts +3 -23
  63. package/dist/components/data/heatmap/heatmap.class.js +2 -2
  64. package/dist/components/data/sequence-strip/sequence-strip.class.d.ts +3 -9
  65. package/dist/components/data/sequence-strip/sequence-strip.class.js +3 -3
  66. package/dist/components/data/table/table.class.d.ts +5 -14
  67. package/dist/components/data/table/table.class.js +6 -6
  68. package/dist/components/forms/button/button.class.d.ts +2 -2
  69. package/dist/components/forms/button/button.class.js +9 -9
  70. package/dist/components/forms/combobox/combobox.class.d.ts +1 -4
  71. package/dist/components/forms/combobox/combobox.class.js +2 -2
  72. package/dist/components/forms/input/time-input.class.d.ts +1 -1
  73. package/dist/components/forms/input/time-input.class.js +1 -1
  74. package/dist/components/forms/select/select.class.d.ts +1 -2
  75. package/dist/components/forms/swatch-picker/swatch-picker.class.d.ts +3 -1
  76. package/dist/components/forms/swatch-picker/swatch-picker.class.js +1 -1
  77. package/dist/components/layout/app-rail/app-rail-item.class.d.ts +3 -6
  78. package/dist/components/layout/app-rail/app-rail-item.class.js +1 -1
  79. package/dist/components/layout/app-rail/app-rail.class.d.ts +2 -4
  80. package/dist/components/layout/app-rail/app-rail.class.js +1 -1
  81. package/dist/components/layout/app-rail-group/app-rail-group.class.d.ts +2 -6
  82. package/dist/components/layout/app-rail-group/app-rail-group.class.js +1 -1
  83. package/dist/components/layout/carousel/carousel.class.d.ts +1 -4
  84. package/dist/components/layout/carousel/carousel.class.js +2 -2
  85. package/dist/components/layout/command-palette/command-palette.class.js +1 -1
  86. package/dist/components/layout/details/details.class.d.ts +2 -4
  87. package/dist/components/layout/details/details.class.js +1 -1
  88. package/dist/components/layout/dock-panel/dock-panel.class.d.ts +3 -7
  89. package/dist/components/layout/dock-panel/dock-panel.class.js +1 -1
  90. package/dist/components/layout/menu/menu.class.js +1 -1
  91. package/dist/components/layout/multi-split/multi-split.class.d.ts +2 -4
  92. package/dist/components/layout/multi-split/multi-split.class.js +1 -1
  93. package/dist/components/layout/navigation-menu-item/navigation-menu-item.class.d.ts +2 -4
  94. package/dist/components/layout/navigation-menu-item/navigation-menu-item.class.js +1 -1
  95. package/dist/components/layout/reorder-list/reorder-item.class.d.ts +1 -4
  96. package/dist/components/layout/reorder-list/reorder-item.class.js +2 -2
  97. package/dist/components/media/attachment-trigger/attachment-trigger.class.d.ts +5 -9
  98. package/dist/components/media/attachment-trigger/attachment-trigger.class.js +5 -5
  99. package/dist/components/media/av-player/av-player.class.js +3 -3
  100. package/dist/components/media/file-input/file-input.class.d.ts +3 -6
  101. package/dist/components/media/file-input/file-input.class.js +3 -3
  102. package/dist/components/media/lightbox/lightbox.class.d.ts +11 -20
  103. package/dist/components/media/lightbox/lightbox.class.js +1 -1
  104. package/dist/components/media/map/map-data-layers.d.ts +20 -0
  105. package/dist/components/media/map/map-data-layers.js +1 -0
  106. package/dist/components/media/map/map.class.js +3 -3
  107. package/dist/components/media/media-time.d.ts +2 -0
  108. package/dist/components/media/media-time.js +1 -0
  109. package/dist/components/media/video/video.class.js +5 -5
  110. package/dist/components/media/video-playlist/video-playlist.class.js +2 -2
  111. package/dist/components/overlays/callout/callout.class.d.ts +2 -5
  112. package/dist/components/overlays/callout/callout.class.js +2 -2
  113. package/dist/components/overlays/dialog/dialog.class.d.ts +4 -7
  114. package/dist/components/overlays/dialog/dialog.class.js +2 -2
  115. package/dist/components/overlays/overlay/dropdown.class.d.ts +5 -10
  116. package/dist/components/overlays/overlay/dropdown.class.js +2 -2
  117. package/dist/components/overlays/overlay/popover.class.d.ts +2 -3
  118. package/dist/components/overlays/popup/popup.class.js +1 -1
  119. package/dist/components/overlays/progress/progress-bar.class.d.ts +1 -4
  120. package/dist/components/overlays/progress/progress-bar.class.js +2 -2
  121. package/dist/components/overlays/progress/progress-ring.class.d.ts +1 -4
  122. package/dist/components/overlays/progress/progress-ring.class.js +2 -2
  123. package/dist/components/overlays/progress/progress-shared.d.ts +3 -3
  124. package/dist/components/overlays/progress/progress-shared.js +1 -1
  125. package/dist/components/overlays/toast/toast-item.class.js +1 -1
  126. package/dist/components/retrieval/graph/graph.class.d.ts +5 -10
  127. package/dist/components/retrieval/graph/graph.class.js +1 -1
  128. package/dist/components/retrieval/knowledge-base/knowledge-base.class.d.ts +7 -14
  129. package/dist/components/retrieval/knowledge-base/knowledge-base.class.js +2 -3
  130. package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.d.ts +2 -8
  131. package/dist/components/retrieval/memory-panel/memory-panel.class.js +0 -4
  132. package/dist/components/retrieval/rag-answer/rag-answer.class.js +1 -1
  133. package/dist/components/utility/export-button/export-button.class.d.ts +6 -10
  134. package/dist/components/utility/export-button/export-button.class.js +1 -1
  135. package/dist/components/utility/mention-popover/mention-popover.class.js +1 -1
  136. package/dist/components/viewers/geojson-view/geojson-view.class.d.ts +1 -1
  137. package/dist/components/viewers/highlight-layer/highlight-layer-timing.js +1 -1
  138. package/dist/components/viewers/pdf-viewer/pdf-viewer.class.js +4 -4
  139. package/dist/events.d.ts +17 -78
  140. package/dist/framework-types.d.ts +1 -1
  141. package/dist/internal/catalog-picker.js +1 -1
  142. package/dist/internal/css-motion-time.d.ts +7 -0
  143. package/dist/internal/css-motion-time.js +1 -0
  144. package/dist/internal/data-state-renderer.d.ts +1 -3
  145. package/dist/internal/data-state-renderer.js +1 -1
  146. package/dist/internal/localization-runtime.d.ts +2 -0
  147. package/dist/internal/localization-runtime.js +1 -1
  148. package/dist/internal/lyra-element.js +2 -2
  149. package/dist/internal/package-metadata.d.ts +1 -1
  150. package/dist/internal/package-metadata.js +1 -1
  151. package/dist/looks/data.css +1 -1
  152. package/dist/looks/terminal.css +1 -1
  153. package/dist/lyra.d.ts +1 -592
  154. package/dist/lyra.js +1 -1
  155. package/dist/testing/event-factory.d.ts +2 -2
  156. package/dist/testing/interaction-drivers.d.ts +5 -3
  157. package/dist/testing/lyra-tag-event-map.js +1 -1
  158. package/dist/theme/looks/data.d.ts +1 -1
  159. package/dist/theme/looks/data.js +1 -1
  160. package/dist/theme/looks/terminal.d.ts +1 -1
  161. package/dist/theme/looks/terminal.js +1 -1
  162. package/dist/theme/theme.d.ts +2 -80
  163. package/dist/theme/theme.js +1 -1
  164. package/llms/components/lr-agent-workspace.md +24 -2
  165. package/llms/components/lr-alert.md +1 -1
  166. package/llms/components/lr-animation.md +1 -1
  167. package/llms/components/lr-app-rail.md +8 -10
  168. package/llms/components/lr-approval-queue.md +2 -4
  169. package/llms/components/lr-attachment-trigger.md +3 -4
  170. package/llms/components/lr-badge.md +2 -2
  171. package/llms/components/lr-button.md +45 -99
  172. package/llms/components/lr-callout.md +3 -4
  173. package/llms/components/lr-carousel.md +3 -5
  174. package/llms/components/lr-chat-message.md +1 -2
  175. package/llms/components/lr-claim-evidence.md +1 -1
  176. package/llms/components/lr-code-block-core.md +2 -2
  177. package/llms/components/lr-code-block.md +5 -6
  178. package/llms/components/lr-color-picker.md +1 -1
  179. package/llms/components/lr-combobox.md +5 -6
  180. package/llms/components/lr-compare-panel.md +2 -4
  181. package/llms/components/lr-confirm-bar.md +3 -7
  182. package/llms/components/lr-context-inspector.md +3 -2
  183. package/llms/components/lr-context-menu.md +2 -2
  184. package/llms/components/lr-context-meter.md +2 -4
  185. package/llms/components/lr-copy-button.md +1 -1
  186. package/llms/components/lr-data-grid.md +4 -7
  187. package/llms/components/lr-details.md +3 -3
  188. package/llms/components/lr-dialog.md +4 -6
  189. package/llms/components/lr-dock-panel.md +5 -6
  190. package/llms/components/lr-document-library.md +5 -7
  191. package/llms/components/lr-drawer.md +3 -3
  192. package/llms/components/lr-dropdown.md +4 -5
  193. package/llms/components/lr-eval-run.md +3 -5
  194. package/llms/components/lr-export-button.md +4 -7
  195. package/llms/components/lr-file-input.md +0 -5
  196. package/llms/components/lr-funnel.md +1 -1
  197. package/llms/components/lr-graph.md +3 -7
  198. package/llms/components/lr-icon.md +1 -1
  199. package/llms/components/lr-include.md +1 -1
  200. package/llms/components/lr-input.md +31 -79
  201. package/llms/components/lr-json-viewer.md +1 -1
  202. package/llms/components/lr-knowledge-base.md +4 -6
  203. package/llms/components/lr-knowledge-graph-explorer.md +2 -5
  204. package/llms/components/lr-known-date.md +1 -1
  205. package/llms/components/lr-lightbox.md +9 -7
  206. package/llms/components/lr-lite-chart.md +1 -5
  207. package/llms/components/lr-markdown-core.md +3 -4
  208. package/llms/components/lr-markdown.md +3 -5
  209. package/llms/components/lr-mcp-app.md +1 -1
  210. package/llms/components/lr-message-actions.md +2 -3
  211. package/llms/components/lr-message-feedback.md +1 -3
  212. package/llms/components/lr-message-parts.md +4 -5
  213. package/llms/components/lr-multi-split.md +8 -7
  214. package/llms/components/lr-native-time-input.md +1 -1
  215. package/llms/components/lr-navigation-menu.md +5 -5
  216. package/llms/components/lr-number-input.md +3 -1
  217. package/llms/components/lr-otp-input.md +2 -0
  218. package/llms/components/lr-page.md +1 -1
  219. package/llms/components/lr-pan-zoom.md +1 -1
  220. package/llms/components/lr-phone-input.md +3 -1
  221. package/llms/components/lr-popover.md +2 -2
  222. package/llms/components/lr-progress-bar.md +3 -5
  223. package/llms/components/lr-progress-ring.md +5 -7
  224. package/llms/components/lr-prompt-input.md +1 -1
  225. package/llms/components/lr-prompt-queue.md +1 -1
  226. package/llms/components/lr-prompt-studio.md +1 -1
  227. package/llms/components/lr-rag-eval-dashboard.md +1 -1
  228. package/llms/components/lr-rating.md +2 -2
  229. package/llms/components/lr-realtime-session.md +1 -1
  230. package/llms/components/lr-reorder-item.md +1 -1
  231. package/llms/components/lr-reorder-list.md +2 -3
  232. package/llms/components/lr-retrieval-compare.md +1 -1
  233. package/llms/components/lr-select.md +3 -1
  234. package/llms/components/lr-selection-toolbar.md +1 -1
  235. package/llms/components/lr-sequence-strip.md +3 -8
  236. package/llms/components/lr-span-waterfall.md +1 -1
  237. package/llms/components/lr-split-panel.md +1 -1
  238. package/llms/components/lr-streaming-text-core.md +2 -1
  239. package/llms/components/lr-streaming-text.md +3 -2
  240. package/llms/components/lr-subagent-panel.md +1 -1
  241. package/llms/components/lr-swatch-picker.md +7 -6
  242. package/llms/components/lr-switch.md +3 -1
  243. package/llms/components/lr-table.md +33 -85
  244. package/llms/components/lr-terminal.md +2 -5
  245. package/llms/components/lr-textarea.md +1 -1
  246. package/llms/components/lr-thread-list.md +8 -14
  247. package/llms/components/lr-time-input.md +1 -1
  248. package/llms/components/lr-toggle-group.md +2 -2
  249. package/llms/components/lr-toggle.md +1 -1
  250. package/llms/components/lr-tool-approval-dialog.md +4 -7
  251. package/llms/components/lr-tool-call-block.md +1 -1
  252. package/llms/components/lr-tool-timeline.md +2 -5
  253. package/llms/components/lr-video-playlist.md +1 -1
  254. package/llms/components/lr-video.md +1 -1
  255. package/llms/components/lr-widget-renderer.md +7 -6
  256. package/llms/components/lr-zoomable-frame.md +1 -1
  257. package/llms/index.md +3 -618
  258. package/llms/migration.md +614 -614
  259. package/llms/shared/events-and-types.md +32 -31
  260. package/llms/shared/frameworks-and-ssr.md +7 -3
  261. package/llms/shared/imports-and-registration.md +22 -22
  262. package/llms/shared/localization-and-rtl.md +2 -2
  263. package/llms/shared/native-styles-and-utilities.md +263 -0
  264. package/llms/shared/styles-and-tokens.md +85 -676
  265. package/llms/shared/testing-and-utilities.md +12 -11
  266. package/llms/shared/v23-to-v24-migration.md +167 -0
  267. package/llms/shared.md +149 -494
  268. package/llms/tokens.md +3 -3
  269. package/llms.txt +9 -4
  270. package/package.json +7 -311
  271. package/registrations.json +1 -1
  272. package/vscode-html-data.json +1 -1
  273. package/web-types.json +1 -1
  274. package/dist/ssr-loader.d.ts +0 -2
  275. package/dist/ssr-loader.js +0 -1
  276. package/dist/theme/presets/shadcn.d.ts +0 -22
  277. package/dist/theme/presets/shadcn.js +0 -1
  278. package/dist/theme/presets.d.ts +0 -49
  279. package/dist/theme/presets.js +0 -1
  280. package/dist/themes/shadcn.css +0 -1
@@ -1 +1 @@
1
- const LYRA_RENAME_LEDGER_SCHEMA_VERSION=1,UNRELEASED_VERSION="unreleased",LYRA_RENAME_PROFILES=Object.freeze([Object.freeze({origin:"lyra-v21",fromMajor:21,toMajor:22,aliasRemovalMajor:23}),Object.freeze({origin:"lyra-v22",fromMajor:22,toMajor:23,aliasRemovalMajor:24})]),LYRA_RENAME_ORIGINS=Object.freeze(LYRA_RENAME_PROFILES.map(profile=>profile.origin)),RENAME_KINDS=Object.freeze(["attribute","property","event","part","css-property","slot"]),REVIEW_KINDS=Object.freeze(["component","attribute","property","event","slot","part","css-property","css-state","method"]),EXPOSURE_KINDS=Object.freeze(["event","part","css-property"]),SURFACE_SECTIONS=Object.freeze({attribute:"attributes",property:"properties",event:"events",part:"parts","css-property":"cssProperties",slot:"slots","css-state":"cssStates",method:"methods"}),TAG_PATTERN=/^lr-[a-z0-9]+(?:-[a-z0-9]+)*$/,ELEMENT_PATTERN=/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/,JS_IDENTIFIER_PATTERN=/^[A-Za-z_$][\w$]*$/,VERSION_PATTERN=/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/,NAME_PATTERNS=Object.freeze({component:TAG_PATTERN,attribute:/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/,property:JS_IDENTIFIER_PATTERN,method:JS_IDENTIFIER_PATTERN,event:/^lr-[a-z0-9]+(?:-[a-z0-9]+)*$/,part:/^[A-Za-z0-9_-]+$/,slot:/^(?:[A-Za-z0-9_-]+)?$/,"css-property":/^--[A-Za-z0-9_-]+$/,"css-state":/^[a-z][a-z0-9-]*$/}),MODULE_REVIEW_KINDS=["entry-point","stylesheet","function","type","constant","class","window-event","root-attribute"],MODULE_PATH_PATTERN=/^\.\/[A-Za-z0-9_./-]+\.(?:js|css)$/,moduleReviewKey=entry=>[entry.kind,entry.module??"",entry.name].join("\0"),PROFILE_LIST_KEYS=["renames","defaults","detailChanges","propertyChanges","retiredEvents","reviews","slotContent","moduleReviews"],AUTHORED_PROFILE_KEYS=["origin","fromMajor","toMajor","aliasRemovalMajor",...PROFILE_LIST_KEYS],PROJECTED_PROFILE_KEYS=[...AUTHORED_PROFILE_KEYS,"exposure"];function isPlainObject(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function unknownKeys(value,allowed){return Object.keys(value).filter(key=>!allowed.includes(key))}function compareText(left,right){return left<right?-1:left>right?1:0}function parseVersion(version){const match=VERSION_PATTERN.exec(String(version??""));return match?match.slice(1,4).map(Number):null}function compareVersions(left,right){const a=parseVersion(left),b=parseVersion(right);if(!a||!b)return null;for(let index=0;index<3;index+=1)if(a[index]!==b[index])return a[index]-b[index];return 0}function majorOf(version){return parseVersion(version)?.[0]??null}function emptyRenameLedger(){return{schemaVersion:1,profiles:LYRA_RENAME_PROFILES.map(header=>({...header,...Object.fromEntries(PROFILE_LIST_KEYS.map(list=>[list,[]]))}))}}function emptyRenameProjection(){return{schemaVersion:1,profiles:emptyRenameLedger().profiles.map(profile=>({...profile,exposure:Object.fromEntries(EXPOSURE_KINDS.map(kind=>[kind,{}]))}))}}function entryLabel(origin,list,entry){return list==="moduleReviews"?`${origin}: module review ${entry?.kind} ${entry?.module??""} ${entry?.name}`:list==="renames"?`${origin}: rename ${entry?.tag} ${entry?.kind} ${entry?.from}`:list==="defaults"?`${origin}: default ${entry?.tag} ${entry?.attribute}`:list==="retiredEvents"?`${origin}: retired event ${entry?.tag} ${entry?.event}`:list==="detailChanges"?`${origin}: detail change ${entry?.tag} ${entry?.event}`:list==="propertyChanges"?`${origin}: property change ${entry?.tag} ${entry?.property}`:list==="slotContent"?`${origin}: slot content ${entry?.tag} ${JSON.stringify(entry?.slot??null)}`:`${origin}: review ${entry?.tag} ${entry?.kind} ${JSON.stringify(entry?.name??null)}`}function sortKey(list,entry){return list==="moduleReviews"?moduleReviewKey(entry):list==="renames"?[entry.tag,entry.kind,entry.from].join("\0"):list==="defaults"?[entry.tag,entry.attribute].join("\0"):list==="detailChanges"||list==="retiredEvents"?[entry.tag,entry.event].join("\0"):list==="propertyChanges"?[entry.tag,entry.property].join("\0"):list==="slotContent"?[entry.tag,entry.slot].join("\0"):[entry.tag,entry.kind,entry.name].join("\0")}function validateName(findings,label,kind,field,value){return typeof value!="string"||!NAME_PATTERNS[kind]?.test(value)?(findings.push(`${label}: ${field} must be a valid ${kind} name, got ${JSON.stringify(value)}`),!1):!0}function validateProjectedSince(findings,label,entry,projected){projected&&entry.since!==UNRELEASED_VERSION&&!parseVersion(entry.since)&&findings.push(`${label}: since must be a version or ${JSON.stringify(UNRELEASED_VERSION)}`)}function validateRemovedIn(findings,label,entry){Object.hasOwn(entry,"removedIn")&&(!parseVersion(entry.removedIn)||compareVersions(entry.removedIn,entry.removalNotBefore)<0)&&findings.push(`${label}: removedIn must be a version at or after removalNotBefore`)}function validateModuleReviewEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["kind","module","name",...projected?["replacement","since","removalNotBefore","removedIn"]:[]]);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),MODULE_REVIEW_KINDS.includes(entry.kind)||findings.push(`${label}: unsupported module review kind ${entry.kind}`),entry.kind==="entry-point"||entry.kind==="stylesheet")Object.hasOwn(entry,"module")&&findings.push(`${label}: path reviews use name, not module`),typeof entry.name!="string"||!MODULE_PATH_PATTERN.test(entry.name)||entry.name.includes("..")?findings.push(`${label}: name must be a package subpath`):entry.name.endsWith(".css")!==(entry.kind==="stylesheet")&&findings.push(`${label}: stylesheet reviews must name CSS; entry-point reviews must name JavaScript`);else{entry.module!=="."&&(typeof entry.module!="string"||!MODULE_PATH_PATTERN.test(entry.module)||!entry.module.endsWith(".js")||entry.module.includes(".."))&&findings.push(`${label}: module must be a JavaScript package subpath`);const pattern=entry.kind==="window-event"?NAME_PATTERNS.event:entry.kind==="root-attribute"?/^data-lr-[a-z0-9-]+$/:JS_IDENTIFIER_PATTERN;(typeof entry.name!="string"||!pattern.test(entry.name))&&findings.push(`${label}: invalid module review name`)}projected&&((typeof entry.replacement!="string"||!entry.replacement.trim())&&findings.push(`${label}: replacement text missing`),parseVersion(entry.removalNotBefore)||findings.push(`${label}: removalNotBefore must be a version`),validateRemovedIn(findings,label,entry)),validateProjectedSince(findings,label,entry,projected)}function validateRenameEntry(findings,label,entry,projected){const allowed=["tag","kind","from","to","polarity",...projected?["since","reflects"]:[]],unknown=unknownKeys(entry,allowed);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),!RENAME_KINDS.includes(entry.kind)){findings.push(`${label}: kind must be one of ${RENAME_KINDS.join(", ")}`);return}const validFrom=validateName(findings,label,entry.kind,"from",entry.from),validTo=validateName(findings,label,entry.kind,"to",entry.to);validFrom&&validTo&&entry.from===entry.to&&findings.push(`${label}: from and to must differ`),entry.kind==="slot"&&(entry.from===""||entry.to==="")&&findings.push(`${label}: the default slot can be reviewed but not renamed`),Object.hasOwn(entry,"polarity")&&(entry.polarity!=="inverted"&&findings.push(`${label}: polarity may only be "inverted"`),entry.kind!=="attribute"&&entry.kind!=="property"&&findings.push(`${label}: only attribute and property renames can invert polarity`)),validateProjectedSince(findings,label,entry,projected),projected&&Object.hasOwn(entry,"reflects")!==(entry.kind==="attribute")&&findings.push(`${label}: reflects is recorded for attribute renames only`),projected&&entry.kind==="attribute"&&typeof entry.reflects!="boolean"&&findings.push(`${label}: reflects must be a boolean`)}function validateDefaultEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","attribute","value",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"attribute","attribute",entry.attribute),Object.hasOwn(entry,"value")?typeof entry.value!="string"&&entry.value!==!0&&!(typeof entry.value=="number"&&Number.isFinite(entry.value))&&findings.push(`${label}: value must be a string, a finite number, or true (presence)`):findings.push(`${label}: needs a value`),validateProjectedSince(findings,label,entry,projected)}function validateSummary(findings,label,summary){(typeof summary!="string"||summary.trim().length<16||/\n/.test(summary))&&findings.push(`${label}: summary must be a single-line description of the change`)}function validateDetailEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","event","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"event","event",entry.event),validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validateRetiredEventEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","event","replacement","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"event","event",entry.event),validateName(findings,label,"event","replacement",entry.replacement),entry.event===entry.replacement&&findings.push(`${label}: replacement must differ from the retired event`),validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validatePropertyChangeEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","property","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"property","property",entry.property),validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validateSlotContentEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","slot","report","allow","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"slot","slot",entry.slot);const lists=["report","allow"].filter(key=>Object.hasOwn(entry,key));if(lists.length!==1)findings.push(`${label}: needs exactly one of report (elements to report) or allow (the only elements kept)`);else{const elements=entry[lists[0]];(!Array.isArray(elements)||!elements.length||!elements.every(element=>typeof element=="string"&&ELEMENT_PATTERN.test(element))||JSON.stringify(elements)!==JSON.stringify([...new Set(elements)].sort(compareText)))&&findings.push(`${label}: ${lists[0]} must be a sorted, unique list of lowercase element names`)}validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validateReviewEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,projected?["tag","kind","name","replacement","removalNotBefore","since","removedIn"]:["tag","kind","name"]);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),!REVIEW_KINDS.includes(entry.kind)){findings.push(`${label}: kind must be one of ${REVIEW_KINDS.join(", ")}`);return}validateName(findings,label,entry.kind,"name",entry.name)&&entry.kind==="component"&&entry.name!==entry.tag&&findings.push(`${label}: a component review must name its own tag`),projected&&((typeof entry.replacement!="string"||!entry.replacement)&&findings.push(`${label}: replacement text missing`),majorOf(entry.removalNotBefore)===null&&findings.push(`${label}: removalNotBefore must be a version`),validateRemovedIn(findings,label,entry)),validateProjectedSince(findings,label,entry,projected)}function requiredExposure(profile){const required=new Map(EXPOSURE_KINDS.map(kind=>[kind,new Map])),need=(kind,name,tag)=>{if(!required.has(kind))return;const tags=required.get(kind).get(name)??new Set;tag&&tags.add(tag),required.get(kind).set(name,tags)};for(const entry of profile.renames??[])need(entry.kind,entry.from,entry.tag),need(entry.kind,entry.to,entry.tag);for(const entry of profile.reviews??[])need(entry.kind,entry.name,entry.tag);for(const entry of profile.detailChanges??[])need("event",entry.event,entry.tag);for(const entry of profile.retiredEvents??[])need("event",entry.event,null),need("event",entry.replacement,entry.tag);for(const entry of profile.propertyChanges??[])need("property",entry.property,entry.tag);return required}function validateExposure(findings,profile){const origin=profile.origin;if(!isPlainObject(profile.exposure)){findings.push(`${origin}: exposure must be an object`);return}const unknown=unknownKeys(profile.exposure,EXPOSURE_KINDS);unknown.length&&findings.push(`${origin}: exposure has unknown kind(s) ${unknown.join(", ")}`);const required=requiredExposure(profile);for(const kind of EXPOSURE_KINDS){const recorded=profile.exposure[kind];if(!isPlainObject(recorded)){findings.push(`${origin}: exposure.${kind} must be an object`);continue}for(const[name,tags]of Object.entries(recorded))required.get(kind).has(name)||findings.push(`${origin}: exposure.${kind} records unreferenced name ${name}`),(!Array.isArray(tags)||!tags.every(tag=>typeof tag=="string"&&TAG_PATTERN.test(tag))||JSON.stringify(tags)!==JSON.stringify([...new Set(tags)].sort(compareText)))&&findings.push(`${origin}: exposure.${kind}.${name} must be a sorted, unique tag list`);for(const[name,tags]of required.get(kind)){if(!Object.hasOwn(recorded,name)){findings.push(`${origin}: exposure.${kind}.${name} is missing`);continue}const recordedTags=new Set(recorded[name]??[]);for(const tag of tags)recordedTags.has(tag)||findings.push(`${origin}: exposure.${kind}.${name} must include ${tag}`)}}}function validateRenameLedgerShape(ledger,{projected=!1}={}){const findings=[];if(!isPlainObject(ledger))return["rename ledger must be an object"];const unknownTop=unknownKeys(ledger,projected?["schemaVersion","profiles"]:["$comment","schemaVersion","profiles"]);if(unknownTop.length&&findings.push(`rename ledger has unknown key(s) ${unknownTop.join(", ")}`),ledger.schemaVersion!==1&&findings.push("rename ledger schemaVersion must be 1"),!Array.isArray(ledger.profiles))return[...findings,"rename ledger profiles must be an array"];const origins=ledger.profiles.map(profile=>profile?.origin);JSON.stringify(origins)!==JSON.stringify(LYRA_RENAME_ORIGINS)&&findings.push(`rename ledger profiles must be exactly ${LYRA_RENAME_ORIGINS.join(", ")} in that order`);for(const profile of ledger.profiles){if(!isPlainObject(profile)){findings.push("rename ledger profiles must be objects");continue}const origin=String(profile.origin),unknown=unknownKeys(profile,projected?PROJECTED_PROFILE_KEYS:AUTHORED_PROFILE_KEYS);unknown.length&&findings.push(`${origin}: unknown key(s) ${unknown.join(", ")}`);const header=LYRA_RENAME_PROFILES.find(candidate=>candidate.origin===profile.origin);if(header)for(const field of["fromMajor","toMajor","aliasRemovalMajor"])profile[field]!==header[field]&&findings.push(`${origin}: ${field} must be ${header[field]}`);for(const list of PROFILE_LIST_KEYS){if(["propertyChanges","retiredEvents"].includes(list)&&profile[list]===void 0)continue;if(!Array.isArray(profile[list])){findings.push(`${origin}: ${list} must be an array`);continue}const seen=new Set,keys=[];for(const entry of profile[list]){const label=entryLabel(origin,list,entry);if(!isPlainObject(entry)){findings.push(`${origin}: ${list} entries must be objects`);continue}list!=="moduleReviews"&&(typeof entry.tag!="string"||!TAG_PATTERN.test(entry.tag))&&findings.push(`${label}: tag must be an lr-* element name`),list==="moduleReviews"?(validateModuleReviewEntry(findings,label,entry,projected),projected&&majorOf(entry.removalNotBefore)!==profile.aliasRemovalMajor&&findings.push(`${label}: removalNotBefore must match the profile's aliasRemovalMajor`)):list==="renames"?validateRenameEntry(findings,label,entry,projected):list==="defaults"?validateDefaultEntry(findings,label,entry,projected):list==="detailChanges"?validateDetailEntry(findings,label,entry,projected):list==="retiredEvents"?(validateRetiredEventEntry(findings,label,entry,projected),projected&&entry.since!==`${profile.toMajor}.0.0`&&findings.push(`${label}: since must match the target major release`)):list==="propertyChanges"?validatePropertyChangeEntry(findings,label,entry,projected):list==="slotContent"?validateSlotContentEntry(findings,label,entry,projected):validateReviewEntry(findings,label,entry,projected);const key=sortKey(list,entry);seen.has(key)&&findings.push(`${label}: duplicate entry`),seen.add(key),keys.push(key)}JSON.stringify(keys)!==JSON.stringify([...keys].sort(compareText))&&findings.push(`${origin}: ${list} must be sorted by ${list==="moduleReviews"?"kind, module and name":"tag and name"}`)}if(!Array.isArray(profile.renames)||!Array.isArray(profile.reviews))continue;const sources=new Map;for(const entry of profile.renames){const names=sources.get(entry.kind)??new Set;names.add(entry.from),sources.set(entry.kind,names)}for(const entry of profile.renames)sources.get(entry.kind)?.has(entry.to)&&findings.push(`${entryLabel(origin,"renames",entry)}: target ${entry.to} is itself renamed; rename directly to the final name`);const renamed=new Set(profile.renames.map(entry=>`${entry.tag}\0${entry.kind}\0${entry.from}`));for(const entry of profile.reviews)renamed.has(`${entry.tag}\0${entry.kind}\0${entry.name}`)&&findings.push(`${entryLabel(origin,"reviews",entry)}: a member is either renamed or reviewed, not both`);projected&&validateExposure(findings,profile)}return findings}function componentMap(inventory){return new Map((inventory?.components??[]).map(component=>[component.tag,component]))}function surfaceEntry(component,kind,name){return(component?.surface?.[SURFACE_SECTIONS[kind]]??[]).find(entry=>entry.name===name)??null}function deprecationRecords(component){const records=[...component?.maturity?.deprecations??[]],componentRecord=component?.maturity?.deprecated;return isPlainObject(componentRecord)&&!records.some(record=>record.kind==="component")&&records.push(componentRecord),records}function deprecationRecordFor(component,kind,name){const records=deprecationRecords(component);if(kind==="component")return records.find(record=>record.kind==="component")??null;const direct=records.find(record=>record.kind===kind&&record.name===name);return direct||kind!=="attribute"?direct??null:records.find(record=>record.kind==="property"&&record.attribute===name)??null}function historicalMember(context,tag,kind,name){const key=JSON.stringify(["member",tag,kind,name]);return context?.records?.[key]??context?.records?.[context?.aliases?.[key]]??null}function historicalExport(context,entry){return context?.records?.[JSON.stringify(["export",entry.kind,entry.module??null,entry.name])]??null}function ownerFor(components,context,tag){const current=components.get(tag);return current||(historicalMember(context,tag,"component",tag)?.state==="retired"?context.sourceComponents[tag]:null)}function policyFor(component,context,tag,kind,name){return deprecationRecordFor(component,kind,name)??historicalMember(context,tag,kind,name)?.policy??null}function replacementOwner(components,context,tag,kind,name){const historical=historicalMember(context,tag,kind,name);return components.get(historical?.state==="retired"?historical.replacementOwner:tag)}function replacementMatches(component,kind,record,to){const replacement=record?.replacement;return isPlainObject(replacement)?replacement.kind===kind&&replacement.name===to?!0:kind==="attribute"&&replacement.kind==="property"?surfaceEntry(component,"property",replacement.name)?.attribute===to:!1:!1}function isBooleanMember(entry){return String(entry?.type??"").split("|").map(part=>part.trim()).includes("boolean")}function exposingTags(inventory,kind,name){return(inventory?.components??[]).filter(component=>surfaceEntry(component,kind,name)).map(component=>component.tag).sort(compareText)}const UNMIRRORED_CLASSIFICATIONS=new Set(["conceptual-only","unsupported"]);function mirroredMembers(inventory){const upstreamComponents=new Map;for(const ecosystem of Object.values(inventory?.upstreams??{}))for(const component of ecosystem?.components??[])upstreamComponents.set(component.tag,component);const mirrored=new Map;for(const mapping of inventory?.mappings??[]){if(!mapping?.targetTag||UNMIRRORED_CLASSIFICATIONS.has(mapping.classification))continue;const upstream=upstreamComponents.get(mapping.upstreamTag);for(const[kind,section]of Object.entries(SURFACE_SECTIONS)){const rewrites=mapping.rewrites?.[section]??[],renamedAway=new Set(rewrites.map(rule=>rule?.from)),names=[...rewrites.map(rule=>rule?.to).filter(name=>typeof name=="string"),...(upstream?.surface?.[section]??[]).map(entry=>entry.name).filter(name=>!renamedAway.has(name))];for(const name of names){const key=`${mapping.targetTag}\0${kind}\0${name}`;mirrored.has(key)||mirrored.set(key,mapping.upstreamTag)}}}return mirrored}function validateRenameLedger(ledger,{inventory,exportDeprecations=[],requireCoverage=!1,sharedTokens=null,compatibilityContext=null}){const findings=validateRenameLedgerShape(ledger);if(findings.length)return findings;const components=componentMap(inventory);if(!Array.isArray(exportDeprecations))return["exportDeprecations must be an array"];if(!exportDeprecations.every(isPlainObject))return["exportDeprecations entries must be objects"];const moduleRecords=new Map(exportDeprecations.map(record=>[moduleReviewKey(record),record]));moduleRecords.size!==exportDeprecations.length&&findings.push("exportDeprecations contains duplicate module records");const mirrored=mirroredMembers(inventory),mirrorOf=(tag,kind,name)=>mirrored.get(`${tag}\0${kind}\0${name}`)??null;for(const profile of ledger.profiles){const origin=profile.origin,covered=new Set,release=`${profile.toMajor}.0.0`,removalMajorMatches=record=>majorOf(record?.removalNotBefore)===profile.aliasRemovalMajor,checkRecordWindow=(label,record)=>{if(removalMajorMatches(record)||findings.push(`${label}: its deprecation record must set removalNotBefore to ${profile.aliasRemovalMajor}.0.0`),record?.since===UNRELEASED_VERSION)return;const since=compareVersions(record?.since,release);(since===null||since>0)&&findings.push(`${label}: its deprecation record must start no later than ${release} (since ${JSON.stringify(record?.since)})`)},checkLyraOnly=(label,tag,kind,name)=>{const upstream=mirrorOf(tag,kind,name);upstream&&findings.push(`${label}: ${name} mirrors ${upstream}; mirrored names never change`)},checkSharedToken=(label,kind,name)=>{kind==="css-property"&&sharedTokens?.has(name)&&findings.push(`${label}: ${name} is a canonical design token, not a component-owned custom property`)},presenceDefaults=new Set(ledger.profiles.filter(candidate=>candidate.toMajor<=profile.toMajor).flatMap(candidate=>candidate.defaults).filter(entry=>entry.value===!0).map(entry=>`${entry.tag}\0${entry.attribute}`)),inversionCompanions=new Set;for(const entry of profile.renames){const label=entryLabel(origin,"renames",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}covered.add(`${entry.tag}\0${entry.kind}\0${entry.from}`),checkLyraOnly(label,entry.tag,entry.kind,entry.from),checkSharedToken(label,entry.kind,entry.from),checkSharedToken(label,entry.kind,entry.to);const retired=historicalMember(compatibilityContext,entry.tag,entry.kind,entry.from)?.state==="retired",from=surfaceEntry(retired?compatibilityContext.sourceComponents[entry.tag]:component,entry.kind,entry.from),targetOwner=replacementOwner(components,compatibilityContext,entry.tag,entry.kind,entry.from),to=surfaceEntry(targetOwner,entry.kind,entry.to);from?from.deprecated||findings.push(`${label}: the alias ${entry.from} is not marked deprecated in the manifest`):findings.push(`${label}: the deprecated alias ${entry.from} is not on the public surface`),to?to.deprecated&&findings.push(`${label}: the canonical name ${entry.to} is itself deprecated`):findings.push(`${label}: the canonical name ${entry.to} is not on the public surface`);const record=policyFor(component,compatibilityContext,entry.tag,entry.kind,entry.from);if(record?(checkRecordWindow(label,record),replacementMatches(targetOwner,entry.kind,record,entry.to)||findings.push(`${label}: its deprecation record names a different replacement than ${entry.to}`)):findings.push(`${label}: no deprecation record retires ${entry.from}`),(entry.kind==="attribute"||entry.kind==="property")&&from&&to&&isBooleanMember(from)&&isBooleanMember(to)){const toAttribute=entry.kind==="attribute"?entry.to:to.attribute,preservesPresence=!!toAttribute&&presenceDefaults.has(`${entry.tag}\0${toAttribute}`);entry.polarity==="inverted"?(to.default===!0&&findings.push(`${label}: an inverted rename must target a boolean that defaults to false`),from.default!==!0&&toAttribute&&inversionCompanions.add(`${entry.tag}\0${toAttribute}`),from.default!==!0&&!preservesPresence&&findings.push(`${label}: ${entry.from} defaults to false, so the inverted rename needs a defaults entry inserting ${toAttribute??entry.to} where both are absent`)):from.default===!0&&to.default!==!0&&!preservesPresence&&findings.push(`${label}: ${entry.from} defaults to true and ${entry.to} to false; declare "polarity": "inverted" or preserve the default with a defaults entry`)}else entry.polarity==="inverted"&&from&&to&&findings.push(`${label}: an inverted rename must retire a boolean for a boolean`)}for(const entry of profile.reviews){const label=entryLabel(origin,"reviews",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}covered.add(`${entry.tag}\0${entry.kind}\0${entry.name}`),entry.kind!=="component"&&checkLyraOnly(label,entry.tag,entry.kind,entry.name),checkSharedToken(label,entry.kind,entry.name);const record=policyFor(component,compatibilityContext,entry.tag,entry.kind,entry.name);record?checkRecordWindow(label,record):findings.push(`${label}: no deprecation record retires ${JSON.stringify(entry.name)}`)}for(const entry of profile.detailChanges){const label=entryLabel(origin,"detailChanges",entry),component=ownerFor(components,compatibilityContext,entry.tag);component?surfaceEntry(component,"event",entry.event)?checkLyraOnly(label,entry.tag,"event",entry.event):findings.push(`${label}: the event is not dispatched by ${entry.tag}`):findings.push(`${label}: component is not in the inventory`)}for(const entry of profile.retiredEvents??[]){const label=entryLabel(origin,"retiredEvents",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component)findings.push(`${label}: component is not in the inventory`);else{surfaceEntry(component,"event",entry.event)&&findings.push(`${label}: retired event is still dispatched by ${entry.tag}`),deprecationRecordFor(component,"event",entry.event)&&findings.push(`${label}: a retired event must not keep a current deprecation record`);const target=surfaceEntry(component,"event",entry.replacement);(!target||target.deprecated)&&findings.push(`${label}: replacement is not a current event on ${entry.tag}`),checkLyraOnly(label,entry.tag,"event",entry.event)}}for(const entry of profile.propertyChanges??[]){const label=entryLabel(origin,"propertyChanges",entry),component=ownerFor(components,compatibilityContext,entry.tag);component?surfaceEntry(component,"property",entry.property)?checkLyraOnly(label,entry.tag,"property",entry.property):findings.push(`${label}: the property is not on the public surface`):findings.push(`${label}: component is not in the inventory`)}for(const entry of profile.slotContent){const label=entryLabel(origin,"slotContent",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}surfaceEntry(component,"slot",entry.slot)||findings.push(`${label}: the slot is not on the public surface`);const record=policyFor(component,compatibilityContext,entry.tag,"slot-content",entry.slot);record&&(covered.add(`${entry.tag}\0slot-content\0${entry.slot}`),Array.isArray(record.permittedContent)&&JSON.stringify(entry.allow??null)!==JSON.stringify(record.permittedContent)&&findings.push(`${label}: its deprecation record permits ${record.permittedContent.join(", ")}; allow must list exactly those elements`))}for(const entry of profile.defaults){const label=entryLabel(origin,"defaults",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}const attribute=surfaceEntry(component,"attribute",entry.attribute);if(!attribute){findings.push(`${label}: the attribute is not on the public surface`);continue}inversionCompanions.has(`${entry.tag}\0${entry.attribute}`)||checkLyraOnly(label,entry.tag,"attribute",entry.attribute),entry.value===!0&&!isBooleanMember(attribute)&&findings.push(`${label}: presence insertion requires a boolean attribute`),attribute.hasDefault&&String(attribute.default)===String(entry.value)&&findings.push(`${label}: the value equals the current default, so there is nothing to preserve`)}const coveredModules=new Set;for(const entry of profile.moduleReviews){const label=entryLabel(origin,"moduleReviews",entry),key=moduleReviewKey(entry);coveredModules.add(key);const record=moduleRecords.get(key)??historicalExport(compatibilityContext,entry)?.policy;record?(checkRecordWindow(label,record),!record.replacement?.usage&&!record.replacement?.name&&findings.push(`${label}: canonical replacement is missing`)):findings.push(`${label}: no canonical exportDeprecations record`)}if(!requireCoverage)continue;for(const record of[...exportDeprecations,...Object.values(compatibilityContext?.records??{}).filter(entry=>entry.state==="retired"&&entry.key.scope==="export").map(entry=>entry.policy)])removalMajorMatches(record)&&!coveredModules.has(moduleReviewKey(record))&&findings.push(`${origin}: ${record.kind} ${record.module??""} ${record.name} is removed in ${profile.aliasRemovalMajor}.0.0 but has no moduleReviews entry`);const coverageOwners=new Map((inventory?.components??[]).map(component=>[component.tag,deprecationRecords(component)]));for(const entry of Object.values(compatibilityContext?.records??{}))entry.state!=="retired"||entry.key.scope!=="member"||coverageOwners.set(entry.key.tag,[...coverageOwners.get(entry.key.tag)??[],entry.policy]);for(const[tag,records]of coverageOwners){const component={tag};for(const record of records){if(!removalMajorMatches(record))continue;const keys=[[record.kind,record.kind==="component"?component.tag:record.name]];record.kind==="property"&&record.attribute&&keys.push(["attribute",record.attribute]);for(const[kind,name]of keys)kind!=="component"&&mirrorOf(component.tag,kind,name)||covered.has(`${component.tag}\0${kind}\0${name}`)||findings.push(`${origin}: ${component.tag} ${kind} ${JSON.stringify(name)} is removed in ${profile.aliasRemovalMajor}.0.0 but has no ${kind==="slot-content"?"slotContent":"rename or review"} entry`)}}}return[...new Set(findings)]}function projectRenameLedger(ledger,inventory,{exportDeprecations=[],compatibilityContext=null}={}){const findings=validateRenameLedger(ledger,{inventory,exportDeprecations,compatibilityContext});if(findings.length)throw new Error(`Invalid Lyra rename ledger: ${findings.join("; ")}`);const components=componentMap(inventory),moduleRecords=new Map(exportDeprecations.map(record=>[moduleReviewKey(record),record])),projection={schemaVersion:1,profiles:ledger.profiles.map(profile=>{const release=`${profile.toMajor}.0.0`,exposure=Object.fromEntries(EXPOSURE_KINDS.map(kind=>[kind,{}]));for(const[kind,names]of requiredExposure(profile))for(const name of[...names.keys()].sort(compareText))exposure[kind][name]=[...new Set([...exposingTags(inventory,kind,name),...compatibilityContext?.exposure?.[kind]?.[name]??[]])].sort(compareText);const recordFor=(entry,kind,name)=>policyFor(components.get(entry.tag),compatibilityContext,entry.tag,kind,name);return{origin:profile.origin,fromMajor:profile.fromMajor,toMajor:profile.toMajor,aliasRemovalMajor:profile.aliasRemovalMajor,renames:profile.renames.map(entry=>{const target=surfaceEntry(replacementOwner(components,compatibilityContext,entry.tag,entry.kind,entry.from),entry.kind,entry.to),attribute=entry.kind==="attribute"?entry.to:target?.attribute,preservesDefault=entry.polarity==="inverted"&&profile.defaults.some(rule=>rule.tag===entry.tag&&rule.attribute===attribute),since=recordFor(entry,entry.kind,entry.from).since,beforeRelease=since===UNRELEASED_VERSION||compareVersions(since,release)<0;return{...structuredClone(entry),since:preservesDefault&&beforeRelease?release:since,...entry.kind==="attribute"?{reflects:!!target?.reflects}:{}}}),defaults:profile.defaults.map(entry=>({...structuredClone(entry),since:release})),retiredEvents:(profile.retiredEvents??[]).map(entry=>({...structuredClone(entry),since:release})),detailChanges:profile.detailChanges.map(entry=>({...structuredClone(entry),since:release})),propertyChanges:(profile.propertyChanges??[]).map(entry=>({...structuredClone(entry),since:release})),reviews:profile.reviews.map(entry=>{const record=recordFor(entry,entry.kind,entry.name),retired=historicalMember(compatibilityContext,entry.tag,entry.kind,entry.name);return{...structuredClone(entry),replacement:String(record.replacement?.usage||record.replacement?.name),...retired?.state==="retired"?{removedIn:retired.removedIn}:{},removalNotBefore:record.removalNotBefore,since:record.since}}),slotContent:profile.slotContent.map(entry=>({...structuredClone(entry),since:release})),moduleReviews:profile.moduleReviews.map(entry=>{const retired=historicalExport(compatibilityContext,entry),record=moduleRecords.get(moduleReviewKey(entry))??retired?.policy;return{...structuredClone(entry),replacement:String(record.replacement.usage||record.replacement.name),...retired?.state==="retired"?{removedIn:retired.removedIn}:{},since:record.since,removalNotBefore:record.removalNotBefore}}),exposure}})},shapeFindings=validateRenameLedgerShape(projection,{projected:!0});if(shapeFindings.length)throw new Error(`Invalid Lyra rename projection: ${shapeFindings.join("; ")}`);return projection}function indexBy(entries,keyOf){const index=new Map;for(const entry of entries){const key=keyOf(entry),list=index.get(key)??[];list.push(entry),index.set(key,list)}return index}const ownerKey=(tag,kind,name)=>`${tag}\0${kind}\0${name}`,nameKey=(kind,name)=>`${kind}\0${name}`;function createRenameProfiles(projection,{lyraVersion=null}={}){const findings=validateRenameLedgerShape(projection,{projected:!0});if(findings.length)throw new Error(`Invalid Lyra rename projection: ${findings.join("; ")}`);if(lyraVersion!==null&&!parseVersion(lyraVersion))throw new Error(`Invalid Lyra version: ${lyraVersion}`);const profiles=new Map,availableAcrossProfiles=entry=>lyraVersion===null||entry.since!==UNRELEASED_VERSION&&compareVersions(entry.since,lyraVersion)<=0,allRenames=projection.profiles.flatMap(profile=>profile.renames).filter(availableAcrossProfiles),allDefaults=projection.profiles.flatMap(profile=>profile.defaults).filter(availableAcrossProfiles);for(const full of projection.profiles){const skipped=[],available=list=>(full[list]??[]).filter(entry=>lyraVersion===null||entry.since!==UNRELEASED_VERSION&&compareVersions(entry.since,lyraVersion)<=0?!0:(skipped.push({list,...entry}),!1)).map(entry=>{if(!entry.removedIn||lyraVersion===null||compareVersions(lyraVersion,entry.removedIn)>=0)return entry;const{removedIn,...beforeRemoval}=entry;return beforeRemoval}),data={...full,...Object.fromEntries(PROFILE_LIST_KEYS.map(list=>[list,available(list)]))},renamesByOwner=new Map(data.renames.map(entry=>[ownerKey(entry.tag,entry.kind,entry.from),entry])),renamesByName=indexBy(data.renames,entry=>nameKey(entry.kind,entry.from)),renamesByTarget=indexBy(data.renames,entry=>nameKey(entry.kind,entry.to)),reviewsByOwner=new Map(data.reviews.map(entry=>[ownerKey(entry.tag,entry.kind,entry.name),entry])),reviewsByName=indexBy(data.reviews,entry=>nameKey(entry.kind,entry.name)),detailsByOwner=new Map(data.detailChanges.map(entry=>[ownerKey(entry.tag,"event",entry.event),entry])),detailsByName=indexBy(data.detailChanges,entry=>entry.event),retiredByOwner=new Map(data.retiredEvents.map(entry=>[ownerKey(entry.tag,"event",entry.event),entry])),retiredByName=indexBy(data.retiredEvents,entry=>entry.event),propertiesByOwner=new Map(data.propertyChanges.map(entry=>[ownerKey(entry.tag,"property",entry.property),entry])),exposure=new Map(EXPOSURE_KINDS.map(kind=>[kind,new Map(Object.entries(data.exposure[kind]).map(([name,tags2])=>[name,new Set(tags2)]))])),defaultsByTag=indexBy(data.defaults,entry=>entry.tag),slotContentByTag=indexBy(data.slotContent,entry=>entry.tag),tags=new Set([...data.renames,...data.reviews,...data.detailChanges,...data.retiredEvents,...data.propertyChanges,...data.defaults,...data.slotContent].map(entry=>entry.tag)),renamesNamed=(kind,name)=>renamesByName.get(nameKey(kind,name))??[],exposersOf=(kind,name)=>exposure.get(kind)?.get(name)??new Set,movers=(kind,from,to)=>new Set(renamesNamed(kind,from).filter(entry=>entry.to===to&&!entry.polarity).map(entry=>entry.tag)),subset=(left,right)=>[...left].every(tag=>right.has(tag));profiles.set(data.origin,{origin:data.origin,fromMajor:data.fromMajor,toMajor:data.toMajor,aliasRemovalMajor:data.aliasRemovalMajor,data,skipped,tags,isEmpty:tags.size===0&&data.moduleReviews.length===0,renameFor:(tag,kind,name)=>renamesByOwner.get(ownerKey(tag,kind,name))??null,renamesNamed,renamesOnto:(kind,name)=>renamesByTarget.get(nameKey(kind,name))??[],reviewFor:(tag,kind,name)=>reviewsByOwner.get(ownerKey(tag,kind,name))??null,reviewsNamed:(kind,name)=>reviewsByName.get(nameKey(kind,name))??[],detailFor:(tag,event)=>detailsByOwner.get(ownerKey(tag,"event",event))??null,detailsNamed:event=>detailsByName.get(event)??[],retiredEventFor:(tag,event)=>retiredByOwner.get(ownerKey(tag,"event",event))??null,retiredEventsNamed:event=>retiredByName.get(event)??[],propertyChangeFor:(tag,property)=>propertiesByOwner.get(ownerKey(tag,"property",property))??null,defaultsFor:tag=>defaultsByTag.get(tag)??[],preservedDefaultsFor:tag=>allDefaults.filter(entry=>entry.tag===tag),defaultAliasesFor:(tag,attribute)=>{const aliases=new Set([attribute]);for(const rename of allRenames)rename.tag!==tag||rename.kind!=="attribute"||(rename.from===attribute&&aliases.add(rename.to),rename.to===attribute&&aliases.add(rename.from));return[...aliases]},slotContentFor:tag=>slotContentByTag.get(tag)??[],exposes:(kind,name,tag)=>!!tag&&exposersOf(kind,name).has(tag),exposers:(kind,name)=>[...exposersOf(kind,name)],targetKeepers(kind,from,to){const moving=movers(kind,from,to);return[...exposersOf(kind,to)].filter(tag=>!moving.has(tag))},sourceKeepers(kind,from,to){const moving=movers(kind,from,to);return[...exposersOf(kind,from)].filter(tag=>!moving.has(tag))},isGlobal(kind,from){const entries=renamesNamed(kind,from);if(!entries.length||entries.some(entry=>entry.polarity))return!1;const targets=new Set(entries.map(entry=>entry.to));if(targets.size!==1)return!1;const[to]=targets,moving=movers(kind,from,to);return exposersOf(kind,from).size>0&&subset(exposersOf(kind,from),moving)&&subset(exposersOf(kind,to),moving)},gainedOwners(kind,name){const onto=(renamesByTarget.get(nameKey(kind,name))??[]).filter(entry=>!entry.polarity);if(!onto.length)return[];const gained=new Set(onto.map(entry=>entry.tag));return[...exposersOf(kind,name)].filter(tag=>!gained.has(tag)).length?[...gained].sort(compareText):[]}})}return profiles}export{LYRA_RENAME_ORIGINS,compareVersions,createRenameProfiles,emptyRenameLedger,emptyRenameProjection,mirroredMembers,projectRenameLedger,validateRenameLedger,validateRenameLedgerShape};
1
+ const LYRA_RENAME_LEDGER_SCHEMA_VERSION=1,UNRELEASED_VERSION="unreleased",LYRA_RENAME_PROFILES=Object.freeze([Object.freeze({origin:"lyra-v21",fromMajor:21,toMajor:22,aliasRemovalMajor:23}),Object.freeze({origin:"lyra-v22",fromMajor:22,toMajor:23,aliasRemovalMajor:24})]),LYRA_RENAME_ORIGINS=Object.freeze(LYRA_RENAME_PROFILES.map(profile=>profile.origin)),RENAME_KINDS=Object.freeze(["attribute","property","event","part","css-property","slot"]),REVIEW_KINDS=Object.freeze(["component","attribute","property","event","slot","part","css-property","css-state","method"]),EXPOSURE_KINDS=Object.freeze(["event","part","css-property"]),SURFACE_SECTIONS=Object.freeze({attribute:"attributes",property:"properties",event:"events",part:"parts","css-property":"cssProperties",slot:"slots","css-state":"cssStates",method:"methods"}),TAG_PATTERN=/^lr-[a-z0-9]+(?:-[a-z0-9]+)*$/,ELEMENT_PATTERN=/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/,JS_IDENTIFIER_PATTERN=/^[A-Za-z_$][\w$]*$/,VERSION_PATTERN=/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/,NAME_PATTERNS=Object.freeze({component:TAG_PATTERN,attribute:/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/,property:JS_IDENTIFIER_PATTERN,method:JS_IDENTIFIER_PATTERN,event:/^lr-[a-z0-9]+(?:-[a-z0-9]+)*$/,part:/^[A-Za-z0-9_-]+$/,slot:/^(?:[A-Za-z0-9_-]+)?$/,"css-property":/^--[A-Za-z0-9_-]+$/,"css-state":/^[a-z][a-z0-9-]*$/}),MODULE_REVIEW_KINDS=["entry-point","stylesheet","function","type","constant","class","window-event","root-attribute"],MODULE_PATH_PATTERN=/^\.\/[A-Za-z0-9_./-]+\.(?:js|css)$/,moduleReviewKey=entry=>[entry.kind,entry.module??"",entry.name].join("\0"),PROFILE_LIST_KEYS=["renames","defaults","detailChanges","detailFields","propertyChanges","retiredEvents","reviews","slotContent","moduleReviews"],AUTHORED_PROFILE_KEYS=["origin","fromMajor","toMajor","aliasRemovalMajor",...PROFILE_LIST_KEYS],PROJECTED_PROFILE_KEYS=[...AUTHORED_PROFILE_KEYS,"exposure"];function isPlainObject(value){return value!==null&&typeof value=="object"&&!Array.isArray(value)}function unknownKeys(value,allowed){return Object.keys(value).filter(key=>!allowed.includes(key))}function compareText(left,right){return left<right?-1:left>right?1:0}function parseVersion(version){const match=VERSION_PATTERN.exec(String(version??""));return match?match.slice(1,4).map(Number):null}function compareVersions(left,right){const a=parseVersion(left),b=parseVersion(right);if(!a||!b)return null;for(let index=0;index<3;index+=1)if(a[index]!==b[index])return a[index]-b[index];return 0}function majorOf(version){return parseVersion(version)?.[0]??null}function emptyRenameLedger(){return{schemaVersion:1,profiles:LYRA_RENAME_PROFILES.map(header=>({...header,...Object.fromEntries(PROFILE_LIST_KEYS.filter(list=>list!=="detailFields").map(list=>[list,[]]))}))}}function emptyRenameProjection(){return{schemaVersion:1,profiles:emptyRenameLedger().profiles.map(profile=>({...profile,exposure:Object.fromEntries(EXPOSURE_KINDS.map(kind=>[kind,{}]))}))}}function entryLabel(origin,list,entry){return list==="moduleReviews"?`${origin}: module review ${entry?.kind} ${entry?.module??""} ${entry?.name}`:list==="renames"?`${origin}: rename ${entry?.tag} ${entry?.kind} ${entry?.from}`:list==="defaults"?`${origin}: default ${entry?.tag} ${entry?.attribute}`:list==="retiredEvents"?`${origin}: retired event ${entry?.tag} ${entry?.event}`:list==="detailChanges"?`${origin}: detail change ${entry?.tag} ${entry?.event}`:list==="detailFields"?`${origin}: detail field ${entry?.tag} ${entry?.event} ${entry?.detailType}.${entry?.field}`:list==="propertyChanges"?`${origin}: property change ${entry?.tag} ${entry?.property}`:list==="slotContent"?`${origin}: slot content ${entry?.tag} ${JSON.stringify(entry?.slot??null)}`:`${origin}: review ${entry?.tag} ${entry?.kind} ${JSON.stringify(entry?.name??null)}`}function sortKey(list,entry){return list==="moduleReviews"?moduleReviewKey(entry):list==="renames"?[entry.tag,entry.kind,entry.from].join("\0"):list==="defaults"?[entry.tag,entry.attribute].join("\0"):list==="detailChanges"||list==="retiredEvents"?[entry.tag,entry.event].join("\0"):list==="detailFields"?[entry.tag,entry.event,entry.module,entry.detailType,entry.field].join("\0"):list==="propertyChanges"?[entry.tag,entry.property].join("\0"):list==="slotContent"?[entry.tag,entry.slot].join("\0"):[entry.tag,entry.kind,entry.name].join("\0")}function validateName(findings,label,kind,field,value){return typeof value!="string"||!NAME_PATTERNS[kind]?.test(value)?(findings.push(`${label}: ${field} must be a valid ${kind} name, got ${JSON.stringify(value)}`),!1):!0}function validateProjectedSince(findings,label,entry,projected){projected&&entry.since!==UNRELEASED_VERSION&&!parseVersion(entry.since)&&findings.push(`${label}: since must be a version or ${JSON.stringify(UNRELEASED_VERSION)}`)}function validateRemovedIn(findings,label,entry){Object.hasOwn(entry,"removedIn")&&(!parseVersion(entry.removedIn)||compareVersions(entry.removedIn,entry.removalNotBefore)<0)&&findings.push(`${label}: removedIn must be a version at or after removalNotBefore`)}function validateModuleReviewEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["kind","module","name",...projected?["replacement","since","removalNotBefore","removedIn"]:[]]);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),MODULE_REVIEW_KINDS.includes(entry.kind)||findings.push(`${label}: unsupported module review kind ${entry.kind}`),entry.kind==="entry-point"||entry.kind==="stylesheet")Object.hasOwn(entry,"module")&&findings.push(`${label}: path reviews use name, not module`),typeof entry.name!="string"||!MODULE_PATH_PATTERN.test(entry.name)||entry.name.includes("..")?findings.push(`${label}: name must be a package subpath`):entry.name.endsWith(".css")!==(entry.kind==="stylesheet")&&findings.push(`${label}: stylesheet reviews must name CSS; entry-point reviews must name JavaScript`);else{entry.module!=="."&&(typeof entry.module!="string"||!MODULE_PATH_PATTERN.test(entry.module)||!entry.module.endsWith(".js")||entry.module.includes(".."))&&findings.push(`${label}: module must be a JavaScript package subpath`);const pattern=entry.kind==="window-event"?NAME_PATTERNS.event:entry.kind==="root-attribute"?/^data-lr-[a-z0-9-]+$/:JS_IDENTIFIER_PATTERN;(typeof entry.name!="string"||!pattern.test(entry.name))&&findings.push(`${label}: invalid module review name`)}projected&&((typeof entry.replacement!="string"||!entry.replacement.trim())&&findings.push(`${label}: replacement text missing`),parseVersion(entry.removalNotBefore)||findings.push(`${label}: removalNotBefore must be a version`),validateRemovedIn(findings,label,entry)),validateProjectedSince(findings,label,entry,projected)}function validateRenameEntry(findings,label,entry,projected){const allowed=["tag","kind","from","to","polarity",...projected?["since","reflects"]:[]],unknown=unknownKeys(entry,allowed);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),!RENAME_KINDS.includes(entry.kind)){findings.push(`${label}: kind must be one of ${RENAME_KINDS.join(", ")}`);return}const validFrom=validateName(findings,label,entry.kind,"from",entry.from),validTo=validateName(findings,label,entry.kind,"to",entry.to);validFrom&&validTo&&entry.from===entry.to&&findings.push(`${label}: from and to must differ`),entry.kind==="slot"&&(entry.from===""||entry.to==="")&&findings.push(`${label}: the default slot can be reviewed but not renamed`),Object.hasOwn(entry,"polarity")&&(entry.polarity!=="inverted"&&findings.push(`${label}: polarity may only be "inverted"`),entry.kind!=="attribute"&&entry.kind!=="property"&&findings.push(`${label}: only attribute and property renames can invert polarity`)),validateProjectedSince(findings,label,entry,projected),projected&&Object.hasOwn(entry,"reflects")!==(entry.kind==="attribute")&&findings.push(`${label}: reflects is recorded for attribute renames only`),projected&&entry.kind==="attribute"&&typeof entry.reflects!="boolean"&&findings.push(`${label}: reflects must be a boolean`)}function validateDefaultEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","attribute","value",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"attribute","attribute",entry.attribute),Object.hasOwn(entry,"value")?typeof entry.value!="string"&&entry.value!==!0&&!(typeof entry.value=="number"&&Number.isFinite(entry.value))&&findings.push(`${label}: value must be a string, a finite number, or true (presence)`):findings.push(`${label}: needs a value`),validateProjectedSince(findings,label,entry,projected)}function validateSummary(findings,label,summary){(typeof summary!="string"||summary.trim().length<16||/\n/.test(summary))&&findings.push(`${label}: summary must be a single-line description of the change`)}function validateDetailEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","event","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"event","event",entry.event),validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validateDetailFieldEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","event","module","detailType","field","relation","replacement","role","notice","removalNotBefore","observedIn","removedIn"]);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"event","event",entry.event),(typeof entry.module!="string"||!/^(?:layout|conversation)\/[a-z0-9-]+\/[a-z0-9-]+(?:\.class|-shared)\.ts$/u.test(entry.module))&&findings.push(`${label}: module must be a canonical layout or conversation detail source path`),(typeof entry.detailType!="string"||!JS_IDENTIFIER_PATTERN.test(entry.detailType))&&findings.push(`${label}: detailType must be an exported type name`),(typeof entry.field!="string"||!JS_IDENTIFIER_PATTERN.test(entry.field))&&findings.push(`${label}: field must be a property name`),["equal","inverse"].includes(entry.relation)||findings.push(`${label}: relation must be equal or inverse`),entry.replacement!=="expanded"&&findings.push(`${label}: replacement must be expanded`),(!["request","settled"].includes(entry.role)||typeof entry.event!="string"||entry.role==="request"!==entry.event.endsWith("-request"))&&findings.push(`${label}: role must match whether the event is a request event`),(typeof entry.notice!="string"||!/^\/\*\*[\s\S]*@deprecated\b[\s\S]*\*\/$/u.test(entry.notice))&&findings.push(`${label}: notice must preserve the exact deprecation JSDoc`),entry.removalNotBefore!=="23.0.0"&&findings.push(`${label}: removalNotBefore must preserve the declared 23.0.0 floor`),entry.observedIn!=="22.0.0"&&findings.push(`${label}: observedIn must name the earliest verified packed observation, 22.0.0`),projected&&typeof entry.observedIn=="string"&&!parseVersion(entry.observedIn)&&findings.push(`${label}: observedIn must be a semantic version`),entry.removedIn!==void 0&&entry.removedIn!=="24.0.0"&&findings.push(`${label}: removedIn requires the separately authorized actual 24.0.0 retirement`),entry.removedIn!==void 0){const removal=parseVersion(entry.removedIn),floor=parseVersion(entry.removalNotBefore),observed=parseVersion(entry.observedIn);(!removal||!floor||!observed||removal[0]<floor[0]+1||removal[0]<observed[0]+2)&&findings.push(`${label}: removedIn must clear the declared floor and a full intervening published major`)}}function validateRetiredEventEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","event","replacement","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"event","event",entry.event),validateName(findings,label,"event","replacement",entry.replacement),entry.event===entry.replacement&&findings.push(`${label}: replacement must differ from the retired event`),validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validatePropertyChangeEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","property","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"property","property",entry.property),validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validateSlotContentEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,["tag","slot","report","allow","summary",...projected?["since"]:[]]);unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),validateName(findings,label,"slot","slot",entry.slot);const lists=["report","allow"].filter(key=>Object.hasOwn(entry,key));if(lists.length!==1)findings.push(`${label}: needs exactly one of report (elements to report) or allow (the only elements kept)`);else{const elements=entry[lists[0]];(!Array.isArray(elements)||!elements.length||!elements.every(element=>typeof element=="string"&&ELEMENT_PATTERN.test(element))||JSON.stringify(elements)!==JSON.stringify([...new Set(elements)].sort(compareText)))&&findings.push(`${label}: ${lists[0]} must be a sorted, unique list of lowercase element names`)}validateSummary(findings,label,entry.summary),validateProjectedSince(findings,label,entry,projected)}function validateReviewEntry(findings,label,entry,projected){const unknown=unknownKeys(entry,projected?["tag","kind","name","replacement","removalNotBefore","since","removedIn"]:["tag","kind","name"]);if(unknown.length&&findings.push(`${label}: unknown key(s) ${unknown.join(", ")}`),!REVIEW_KINDS.includes(entry.kind)){findings.push(`${label}: kind must be one of ${REVIEW_KINDS.join(", ")}`);return}validateName(findings,label,entry.kind,"name",entry.name)&&entry.kind==="component"&&entry.name!==entry.tag&&findings.push(`${label}: a component review must name its own tag`),projected&&((typeof entry.replacement!="string"||!entry.replacement)&&findings.push(`${label}: replacement text missing`),majorOf(entry.removalNotBefore)===null&&findings.push(`${label}: removalNotBefore must be a version`),validateRemovedIn(findings,label,entry)),validateProjectedSince(findings,label,entry,projected)}function requiredExposure(profile){const required=new Map(EXPOSURE_KINDS.map(kind=>[kind,new Map])),need=(kind,name,tag)=>{if(!required.has(kind))return;const tags=required.get(kind).get(name)??new Set;tag&&tags.add(tag),required.get(kind).set(name,tags)};for(const entry of profile.renames??[])need(entry.kind,entry.from,entry.tag),need(entry.kind,entry.to,entry.tag);for(const entry of profile.reviews??[])need(entry.kind,entry.name,entry.tag);for(const entry of profile.detailChanges??[])need("event",entry.event,entry.tag);for(const entry of profile.retiredEvents??[])need("event",entry.event,null),need("event",entry.replacement,entry.tag);for(const entry of profile.propertyChanges??[])need("property",entry.property,entry.tag);return required}function validateExposure(findings,profile){const origin=profile.origin;if(!isPlainObject(profile.exposure)){findings.push(`${origin}: exposure must be an object`);return}const unknown=unknownKeys(profile.exposure,EXPOSURE_KINDS);unknown.length&&findings.push(`${origin}: exposure has unknown kind(s) ${unknown.join(", ")}`);const required=requiredExposure(profile);for(const kind of EXPOSURE_KINDS){const recorded=profile.exposure[kind];if(!isPlainObject(recorded)){findings.push(`${origin}: exposure.${kind} must be an object`);continue}for(const[name,tags]of Object.entries(recorded))required.get(kind).has(name)||findings.push(`${origin}: exposure.${kind} records unreferenced name ${name}`),(!Array.isArray(tags)||!tags.every(tag=>typeof tag=="string"&&TAG_PATTERN.test(tag))||JSON.stringify(tags)!==JSON.stringify([...new Set(tags)].sort(compareText)))&&findings.push(`${origin}: exposure.${kind}.${name} must be a sorted, unique tag list`);for(const[name,tags]of required.get(kind)){if(!Object.hasOwn(recorded,name)){findings.push(`${origin}: exposure.${kind}.${name} is missing`);continue}const recordedTags=new Set(recorded[name]??[]);for(const tag of tags)recordedTags.has(tag)||findings.push(`${origin}: exposure.${kind}.${name} must include ${tag}`)}}}function validateRenameLedgerShape(ledger,{projected=!1}={}){const findings=[];if(!isPlainObject(ledger))return["rename ledger must be an object"];const unknownTop=unknownKeys(ledger,projected?["schemaVersion","profiles"]:["$comment","schemaVersion","profiles"]);if(unknownTop.length&&findings.push(`rename ledger has unknown key(s) ${unknownTop.join(", ")}`),ledger.schemaVersion!==1&&findings.push("rename ledger schemaVersion must be 1"),!Array.isArray(ledger.profiles))return[...findings,"rename ledger profiles must be an array"];const origins=ledger.profiles.map(profile=>profile?.origin);JSON.stringify(origins)!==JSON.stringify(LYRA_RENAME_ORIGINS)&&findings.push(`rename ledger profiles must be exactly ${LYRA_RENAME_ORIGINS.join(", ")} in that order`);for(const profile of ledger.profiles){if(!isPlainObject(profile)){findings.push("rename ledger profiles must be objects");continue}const origin=String(profile.origin),unknown=unknownKeys(profile,projected?PROJECTED_PROFILE_KEYS:AUTHORED_PROFILE_KEYS);unknown.length&&findings.push(`${origin}: unknown key(s) ${unknown.join(", ")}`);const header=LYRA_RENAME_PROFILES.find(candidate=>candidate.origin===profile.origin);if(header)for(const field of["fromMajor","toMajor","aliasRemovalMajor"])profile[field]!==header[field]&&findings.push(`${origin}: ${field} must be ${header[field]}`);for(const list of PROFILE_LIST_KEYS){if(["propertyChanges","retiredEvents","detailFields"].includes(list)&&profile[list]===void 0)continue;if(!Array.isArray(profile[list])){findings.push(`${origin}: ${list} must be an array`);continue}const seen=new Set,keys=[];for(const entry of profile[list]){const label=entryLabel(origin,list,entry);if(!isPlainObject(entry)){findings.push(`${origin}: ${list} entries must be objects`);continue}list!=="moduleReviews"&&(typeof entry.tag!="string"||!TAG_PATTERN.test(entry.tag))&&findings.push(`${label}: tag must be an lr-* element name`),list==="moduleReviews"?(validateModuleReviewEntry(findings,label,entry,projected),projected&&majorOf(entry.removalNotBefore)!==profile.aliasRemovalMajor&&findings.push(`${label}: removalNotBefore must match the profile's aliasRemovalMajor`)):list==="renames"?validateRenameEntry(findings,label,entry,projected):list==="defaults"?validateDefaultEntry(findings,label,entry,projected):list==="detailChanges"?validateDetailEntry(findings,label,entry,projected):list==="detailFields"?validateDetailFieldEntry(findings,label,entry,projected):list==="retiredEvents"?(validateRetiredEventEntry(findings,label,entry,projected),projected&&entry.since!==`${profile.toMajor}.0.0`&&findings.push(`${label}: since must match the target major release`)):list==="propertyChanges"?validatePropertyChangeEntry(findings,label,entry,projected):list==="slotContent"?validateSlotContentEntry(findings,label,entry,projected):validateReviewEntry(findings,label,entry,projected);const key=sortKey(list,entry);seen.has(key)&&findings.push(`${label}: duplicate entry`),seen.add(key),keys.push(key)}JSON.stringify(keys)!==JSON.stringify([...keys].sort(compareText))&&findings.push(`${origin}: ${list} must be sorted by ${list==="moduleReviews"?"kind, module and name":"tag and name"}`)}if(!Array.isArray(profile.renames)||!Array.isArray(profile.reviews))continue;const sources=new Map;for(const entry of profile.renames){const names=sources.get(entry.kind)??new Set;names.add(entry.from),sources.set(entry.kind,names)}for(const entry of profile.renames)sources.get(entry.kind)?.has(entry.to)&&findings.push(`${entryLabel(origin,"renames",entry)}: target ${entry.to} is itself renamed; rename directly to the final name`);const renamed=new Set(profile.renames.map(entry=>`${entry.tag}\0${entry.kind}\0${entry.from}`));for(const entry of profile.reviews)renamed.has(`${entry.tag}\0${entry.kind}\0${entry.name}`)&&findings.push(`${entryLabel(origin,"reviews",entry)}: a member is either renamed or reviewed, not both`);projected&&validateExposure(findings,profile)}if(ledger.profiles.length===LYRA_RENAME_PROFILES.length){const historicalFields=ledger.profiles[0]?.detailFields,nextFields=ledger.profiles[1]?.detailFields;(Array.isArray(historicalFields)||Array.isArray(nextFields))&&JSON.stringify(historicalFields??[])!==JSON.stringify(nextFields??[])&&findings.push("lyra-v21 and lyra-v22 detailFields must preserve the same verified field exposures")}return findings}function componentMap(inventory){return new Map((inventory?.components??[]).map(component=>[component.tag,component]))}function surfaceEntry(component,kind,name){return(component?.surface?.[SURFACE_SECTIONS[kind]]??[]).find(entry=>entry.name===name)??null}function deprecationRecords(component){const records=[...component?.maturity?.deprecations??[]],componentRecord=component?.maturity?.deprecated;return isPlainObject(componentRecord)&&!records.some(record=>record.kind==="component")&&records.push(componentRecord),records}function deprecationRecordFor(component,kind,name){const records=deprecationRecords(component);if(kind==="component")return records.find(record=>record.kind==="component")??null;const direct=records.find(record=>record.kind===kind&&record.name===name);return direct||kind!=="attribute"?direct??null:records.find(record=>record.kind==="property"&&record.attribute===name)??null}function historicalMember(context,tag,kind,name){const key=JSON.stringify(["member",tag,kind,name]);return context?.records?.[key]??context?.records?.[context?.aliases?.[key]]??null}function historicalSourceComponent(context,tag,record=null){const historical=record??historicalMember(context,tag,"component",tag);return historical?.state!=="retired"?null:historical.sourceComponent??context?.sourceComponents?.[tag]??null}function historicalExport(context,entry){return context?.records?.[JSON.stringify(["export",entry.kind,entry.module??null,entry.name])]??null}function ownerFor(components,context,tag){const current=components.get(tag);return current||historicalSourceComponent(context,tag)}function policyFor(component,context,tag,kind,name){return deprecationRecordFor(component,kind,name)??historicalMember(context,tag,kind,name)?.policy??null}function replacementOwner(components,context,tag,kind,name){const historical=historicalMember(context,tag,kind,name);return components.get(historical?.state==="retired"?historical.replacementOwner:tag)}function replacementMatches(component,kind,record,to){const replacement=record?.replacement;return isPlainObject(replacement)?replacement.kind===kind&&replacement.name===to?!0:kind==="attribute"&&replacement.kind==="property"?surfaceEntry(component,"property",replacement.name)?.attribute===to:!1:!1}function isBooleanMember(entry){return String(entry?.type??"").split("|").map(part=>part.trim()).includes("boolean")}function exposingTags(inventory,kind,name){return(inventory?.components??[]).filter(component=>surfaceEntry(component,kind,name)).map(component=>component.tag).sort(compareText)}const UNMIRRORED_CLASSIFICATIONS=new Set(["conceptual-only","unsupported"]);function mirroredMembers(inventory){const upstreamComponents=new Map;for(const ecosystem of Object.values(inventory?.upstreams??{}))for(const component of ecosystem?.components??[])upstreamComponents.set(component.tag,component);const mirrored=new Map;for(const mapping of inventory?.mappings??[]){if(!mapping?.targetTag||UNMIRRORED_CLASSIFICATIONS.has(mapping.classification))continue;const upstream=upstreamComponents.get(mapping.upstreamTag);for(const[kind,section]of Object.entries(SURFACE_SECTIONS)){const rewrites=mapping.rewrites?.[section]??[],renamedAway=new Set(rewrites.map(rule=>rule?.from)),names=[...rewrites.map(rule=>rule?.to).filter(name=>typeof name=="string"),...(upstream?.surface?.[section]??[]).map(entry=>entry.name).filter(name=>!renamedAway.has(name))];for(const name of names){const key=`${mapping.targetTag}\0${kind}\0${name}`;mirrored.has(key)||mirrored.set(key,mapping.upstreamTag)}}}return mirrored}function validateRenameLedger(ledger,{inventory,exportDeprecations=[],requireCoverage=!1,sharedTokens=null,compatibilityContext=null}){const findings=validateRenameLedgerShape(ledger);if(findings.length)return findings;const components=componentMap(inventory);if(!Array.isArray(exportDeprecations))return["exportDeprecations must be an array"];if(!exportDeprecations.every(isPlainObject))return["exportDeprecations entries must be objects"];const moduleRecords=new Map(exportDeprecations.map(record=>[moduleReviewKey(record),record]));moduleRecords.size!==exportDeprecations.length&&findings.push("exportDeprecations contains duplicate module records");const mirrored=mirroredMembers(inventory),mirrorOf=(tag,kind,name)=>mirrored.get(`${tag}\0${kind}\0${name}`)??null;for(const profile of ledger.profiles){const origin=profile.origin,covered=new Set,release=`${profile.toMajor}.0.0`,removalMajorMatches=record=>majorOf(record?.removalNotBefore)===profile.aliasRemovalMajor,checkRecordWindow=(label,record)=>{if(removalMajorMatches(record)||findings.push(`${label}: its deprecation record must set removalNotBefore to ${profile.aliasRemovalMajor}.0.0`),record?.since===UNRELEASED_VERSION)return;const since=compareVersions(record?.since,release);(since===null||since>0)&&findings.push(`${label}: its deprecation record must start no later than ${release} (since ${JSON.stringify(record?.since)})`)},checkLyraOnly=(label,tag,kind,name)=>{const upstream=mirrorOf(tag,kind,name);upstream&&findings.push(`${label}: ${name} mirrors ${upstream}; mirrored names never change`)},checkSharedToken=(label,kind,name)=>{kind==="css-property"&&sharedTokens?.has(name)&&findings.push(`${label}: ${name} is a canonical design token, not a component-owned custom property`)},presenceDefaults=new Set(ledger.profiles.filter(candidate=>candidate.toMajor<=profile.toMajor).flatMap(candidate=>candidate.defaults).filter(entry=>entry.value===!0).map(entry=>`${entry.tag}\0${entry.attribute}`)),inversionCompanions=new Set;for(const entry of profile.detailFields??[]){const label=entryLabel(origin,"detailFields",entry),component=ownerFor(components,compatibilityContext,entry.tag);component?surfaceEntry(component,"event",entry.event)||findings.push(`${label}: the event is not dispatched by ${entry.tag}`):findings.push(`${label}: component is not in the inventory`)}for(const entry of profile.renames){const label=entryLabel(origin,"renames",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}covered.add(`${entry.tag}\0${entry.kind}\0${entry.from}`),checkLyraOnly(label,entry.tag,entry.kind,entry.from),checkSharedToken(label,entry.kind,entry.from),checkSharedToken(label,entry.kind,entry.to);const historical=historicalMember(compatibilityContext,entry.tag,entry.kind,entry.from),retired=historical?.state==="retired",from=surfaceEntry(retired?historicalSourceComponent(compatibilityContext,entry.tag,historical):component,entry.kind,entry.from),targetOwner=replacementOwner(components,compatibilityContext,entry.tag,entry.kind,entry.from),to=surfaceEntry(targetOwner,entry.kind,entry.to);from?from.deprecated||findings.push(`${label}: the alias ${entry.from} is not marked deprecated in the manifest`):findings.push(`${label}: the deprecated alias ${entry.from} is not on the public surface`),to?to.deprecated&&findings.push(`${label}: the canonical name ${entry.to} is itself deprecated`):findings.push(`${label}: the canonical name ${entry.to} is not on the public surface`);const record=policyFor(component,compatibilityContext,entry.tag,entry.kind,entry.from);if(record?(checkRecordWindow(label,record),replacementMatches(targetOwner,entry.kind,record,entry.to)||findings.push(`${label}: its deprecation record names a different replacement than ${entry.to}`)):findings.push(`${label}: no deprecation record retires ${entry.from}`),(entry.kind==="attribute"||entry.kind==="property")&&from&&to&&isBooleanMember(from)&&isBooleanMember(to)){const toAttribute=entry.kind==="attribute"?entry.to:to.attribute,preservesPresence=!!toAttribute&&presenceDefaults.has(`${entry.tag}\0${toAttribute}`);entry.polarity==="inverted"?(to.default===!0&&findings.push(`${label}: an inverted rename must target a boolean that defaults to false`),from.default!==!0&&toAttribute&&inversionCompanions.add(`${entry.tag}\0${toAttribute}`),from.default!==!0&&!preservesPresence&&findings.push(`${label}: ${entry.from} defaults to false, so the inverted rename needs a defaults entry inserting ${toAttribute??entry.to} where both are absent`)):from.default===!0&&to.default!==!0&&!preservesPresence&&findings.push(`${label}: ${entry.from} defaults to true and ${entry.to} to false; declare "polarity": "inverted" or preserve the default with a defaults entry`)}else entry.polarity==="inverted"&&from&&to&&findings.push(`${label}: an inverted rename must retire a boolean for a boolean`)}for(const entry of profile.reviews){const label=entryLabel(origin,"reviews",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}covered.add(`${entry.tag}\0${entry.kind}\0${entry.name}`),entry.kind!=="component"&&checkLyraOnly(label,entry.tag,entry.kind,entry.name),checkSharedToken(label,entry.kind,entry.name);const record=policyFor(component,compatibilityContext,entry.tag,entry.kind,entry.name);record?checkRecordWindow(label,record):findings.push(`${label}: no deprecation record retires ${JSON.stringify(entry.name)}`)}for(const entry of profile.detailChanges){const label=entryLabel(origin,"detailChanges",entry),component=ownerFor(components,compatibilityContext,entry.tag);component?surfaceEntry(component,"event",entry.event)?checkLyraOnly(label,entry.tag,"event",entry.event):findings.push(`${label}: the event is not dispatched by ${entry.tag}`):findings.push(`${label}: component is not in the inventory`)}for(const entry of profile.retiredEvents??[]){const label=entryLabel(origin,"retiredEvents",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component)findings.push(`${label}: component is not in the inventory`);else{surfaceEntry(component,"event",entry.event)&&findings.push(`${label}: retired event is still dispatched by ${entry.tag}`),deprecationRecordFor(component,"event",entry.event)&&findings.push(`${label}: a retired event must not keep a current deprecation record`);const target=surfaceEntry(component,"event",entry.replacement);(!target||target.deprecated)&&findings.push(`${label}: replacement is not a current event on ${entry.tag}`),checkLyraOnly(label,entry.tag,"event",entry.event)}}for(const entry of profile.propertyChanges??[]){const label=entryLabel(origin,"propertyChanges",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component)findings.push(`${label}: component is not in the inventory`);else{const historical=historicalMember(compatibilityContext,entry.tag,"property",entry.property),propertyOwner=historical?.state==="retired"?historical.sourceComponent??historicalSourceComponent(compatibilityContext,entry.tag,historical):component;surfaceEntry(propertyOwner,"property",entry.property)?checkLyraOnly(label,entry.tag,"property",entry.property):findings.push(`${label}: the property is not on the public surface`)}}for(const entry of profile.slotContent){const label=entryLabel(origin,"slotContent",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}surfaceEntry(component,"slot",entry.slot)||findings.push(`${label}: the slot is not on the public surface`);const record=policyFor(component,compatibilityContext,entry.tag,"slot-content",entry.slot);record&&(covered.add(`${entry.tag}\0slot-content\0${entry.slot}`),Array.isArray(record.permittedContent)&&JSON.stringify(entry.allow??null)!==JSON.stringify(record.permittedContent)&&findings.push(`${label}: its deprecation record permits ${record.permittedContent.join(", ")}; allow must list exactly those elements`))}for(const entry of profile.defaults){const label=entryLabel(origin,"defaults",entry),component=ownerFor(components,compatibilityContext,entry.tag);if(!component){findings.push(`${label}: component is not in the inventory`);continue}const attribute=surfaceEntry(component,"attribute",entry.attribute);if(!attribute){findings.push(`${label}: the attribute is not on the public surface`);continue}inversionCompanions.has(`${entry.tag}\0${entry.attribute}`)||checkLyraOnly(label,entry.tag,"attribute",entry.attribute),entry.value===!0&&!isBooleanMember(attribute)&&findings.push(`${label}: presence insertion requires a boolean attribute`),attribute.hasDefault&&String(attribute.default)===String(entry.value)&&findings.push(`${label}: the value equals the current default, so there is nothing to preserve`)}const coveredModules=new Set;for(const entry of profile.moduleReviews){const label=entryLabel(origin,"moduleReviews",entry),key=moduleReviewKey(entry);coveredModules.add(key);const record=moduleRecords.get(key)??historicalExport(compatibilityContext,entry)?.policy;record?(checkRecordWindow(label,record),!record.replacement?.usage&&!record.replacement?.name&&findings.push(`${label}: canonical replacement is missing`)):findings.push(`${label}: no canonical exportDeprecations record`)}if(!requireCoverage)continue;for(const record of[...exportDeprecations,...Object.values(compatibilityContext?.records??{}).filter(entry=>entry.state==="retired"&&entry.key.scope==="export").map(entry=>entry.policy)])removalMajorMatches(record)&&!coveredModules.has(moduleReviewKey(record))&&findings.push(`${origin}: ${record.kind} ${record.module??""} ${record.name} is removed in ${profile.aliasRemovalMajor}.0.0 but has no moduleReviews entry`);const coverageOwners=new Map((inventory?.components??[]).map(component=>[component.tag,deprecationRecords(component)]));for(const entry of Object.values(compatibilityContext?.records??{}))entry.state!=="retired"||entry.key.scope!=="member"||coverageOwners.set(entry.key.tag,[...coverageOwners.get(entry.key.tag)??[],entry.policy]);for(const[tag,records]of coverageOwners){const component={tag};for(const record of records){if(!removalMajorMatches(record))continue;const keys=[[record.kind,record.kind==="component"?component.tag:record.name]];record.kind==="property"&&record.attribute&&keys.push(["attribute",record.attribute]);for(const[kind,name]of keys)kind!=="component"&&mirrorOf(component.tag,kind,name)||covered.has(`${component.tag}\0${kind}\0${name}`)||findings.push(`${origin}: ${component.tag} ${kind} ${JSON.stringify(name)} is removed in ${profile.aliasRemovalMajor}.0.0 but has no ${kind==="slot-content"?"slotContent":"rename or review"} entry`)}}}return[...new Set(findings)]}function projectRenameLedger(ledger,inventory,{exportDeprecations=[],compatibilityContext=null}={}){const findings=validateRenameLedger(ledger,{inventory,exportDeprecations,compatibilityContext});if(findings.length)throw new Error(`Invalid Lyra rename ledger: ${findings.join("; ")}`);const components=componentMap(inventory),moduleRecords=new Map(exportDeprecations.map(record=>[moduleReviewKey(record),record])),projection={schemaVersion:1,profiles:ledger.profiles.map(profile=>{const release=`${profile.toMajor}.0.0`,exposure=Object.fromEntries(EXPOSURE_KINDS.map(kind=>[kind,{}]));for(const[kind,names]of requiredExposure(profile))for(const name of[...names.keys()].sort(compareText))exposure[kind][name]=[...new Set([...exposingTags(inventory,kind,name),...compatibilityContext?.exposure?.[kind]?.[name]??[]])].sort(compareText);const recordFor=(entry,kind,name)=>policyFor(components.get(entry.tag),compatibilityContext,entry.tag,kind,name);return{origin:profile.origin,fromMajor:profile.fromMajor,toMajor:profile.toMajor,aliasRemovalMajor:profile.aliasRemovalMajor,renames:profile.renames.map(entry=>{const target=surfaceEntry(replacementOwner(components,compatibilityContext,entry.tag,entry.kind,entry.from),entry.kind,entry.to),attribute=entry.kind==="attribute"?entry.to:target?.attribute,preservesDefault=entry.polarity==="inverted"&&profile.defaults.some(rule=>rule.tag===entry.tag&&rule.attribute===attribute),since=recordFor(entry,entry.kind,entry.from).since,beforeRelease=since===UNRELEASED_VERSION||compareVersions(since,release)<0;return{...structuredClone(entry),since:preservesDefault&&beforeRelease?release:since,...entry.kind==="attribute"?{reflects:!!target?.reflects}:{}}}),defaults:profile.defaults.map(entry=>({...structuredClone(entry),since:release})),retiredEvents:(profile.retiredEvents??[]).map(entry=>({...structuredClone(entry),since:release})),detailChanges:profile.detailChanges.map(entry=>({...structuredClone(entry),since:release})),...Array.isArray(profile.detailFields)?{detailFields:profile.detailFields.map(entry=>structuredClone(entry))}:{},propertyChanges:(profile.propertyChanges??[]).map(entry=>({...structuredClone(entry),since:release})),reviews:profile.reviews.map(entry=>{const record=recordFor(entry,entry.kind,entry.name),retired=historicalMember(compatibilityContext,entry.tag,entry.kind,entry.name);return{...structuredClone(entry),replacement:String(record.replacement?.usage||record.replacement?.name),...retired?.state==="retired"?{removedIn:retired.removedIn}:{},removalNotBefore:record.removalNotBefore,since:record.since}}),slotContent:profile.slotContent.map(entry=>({...structuredClone(entry),since:release})),moduleReviews:profile.moduleReviews.map(entry=>{const retired=historicalExport(compatibilityContext,entry),record=moduleRecords.get(moduleReviewKey(entry))??retired?.policy;return{...structuredClone(entry),replacement:String(record.replacement.usage||record.replacement.name),...retired?.state==="retired"?{removedIn:retired.removedIn}:{},since:record.since,removalNotBefore:record.removalNotBefore}}),exposure}})},shapeFindings=validateRenameLedgerShape(projection,{projected:!0});if(shapeFindings.length)throw new Error(`Invalid Lyra rename projection: ${shapeFindings.join("; ")}`);return projection}function indexBy(entries,keyOf){const index=new Map;for(const entry of entries){const key=keyOf(entry),list=index.get(key)??[];list.push(entry),index.set(key,list)}return index}const ownerKey=(tag,kind,name)=>`${tag}\0${kind}\0${name}`,nameKey=(kind,name)=>`${kind}\0${name}`;function createRenameProfiles(projection,{lyraVersion=null}={}){const findings=validateRenameLedgerShape(projection,{projected:!0});if(findings.length)throw new Error(`Invalid Lyra rename projection: ${findings.join("; ")}`);if(lyraVersion!==null&&!parseVersion(lyraVersion))throw new Error(`Invalid Lyra version: ${lyraVersion}`);const profiles=new Map,availableAcrossProfiles=entry=>lyraVersion===null||entry.since!==UNRELEASED_VERSION&&compareVersions(entry.since,lyraVersion)<=0,allRenames=projection.profiles.flatMap(profile=>profile.renames).filter(availableAcrossProfiles),allDefaults=projection.profiles.flatMap(profile=>profile.defaults).filter(availableAcrossProfiles);for(const full of projection.profiles){const skipped=[],available=list=>(full[list]??[]).filter(entry=>{if(lyraVersion===null)return!0;const availableFrom=list==="detailFields"?entry.observedIn:entry.since;return availableFrom!==UNRELEASED_VERSION&&compareVersions(availableFrom,lyraVersion)<=0?!0:(skipped.push({list,...entry}),!1)}).map(entry=>{if(!entry.removedIn||lyraVersion===null||compareVersions(lyraVersion,entry.removedIn)>=0)return entry;const{removedIn,...beforeRemoval}=entry;return beforeRemoval}),data={...full,...Object.fromEntries(PROFILE_LIST_KEYS.map(list=>[list,available(list)]))},renamesByOwner=new Map(data.renames.map(entry=>[ownerKey(entry.tag,entry.kind,entry.from),entry])),renamesByName=indexBy(data.renames,entry=>nameKey(entry.kind,entry.from)),renamesByTarget=indexBy(data.renames,entry=>nameKey(entry.kind,entry.to)),reviewsByOwner=new Map(data.reviews.map(entry=>[ownerKey(entry.tag,entry.kind,entry.name),entry])),reviewsByName=indexBy(data.reviews,entry=>nameKey(entry.kind,entry.name)),detailsByOwner=new Map(data.detailChanges.map(entry=>[ownerKey(entry.tag,"event",entry.event),entry])),detailsByName=indexBy(data.detailChanges,entry=>entry.event),fieldsByOwnerEvent=new Map((data.detailFields??[]).map(entry=>[ownerKey(entry.tag,"event",entry.event),entry])),fieldsByEvent=indexBy(data.detailFields??[],entry=>entry.event),retiredByOwner=new Map(data.retiredEvents.map(entry=>[ownerKey(entry.tag,"event",entry.event),entry])),retiredByName=indexBy(data.retiredEvents,entry=>entry.event),propertiesByOwner=new Map(data.propertyChanges.map(entry=>[ownerKey(entry.tag,"property",entry.property),entry])),exposure=new Map(EXPOSURE_KINDS.map(kind=>[kind,new Map(Object.entries(data.exposure[kind]).map(([name,tags2])=>[name,new Set(tags2)]))])),defaultsByTag=indexBy(data.defaults,entry=>entry.tag),slotContentByTag=indexBy(data.slotContent,entry=>entry.tag),tags=new Set([...data.renames,...data.reviews,...data.detailChanges,...data.detailFields??[],...data.retiredEvents,...data.propertyChanges,...data.defaults,...data.slotContent].map(entry=>entry.tag)),renamesNamed=(kind,name)=>renamesByName.get(nameKey(kind,name))??[],exposersOf=(kind,name)=>exposure.get(kind)?.get(name)??new Set,movers=(kind,from,to)=>new Set(renamesNamed(kind,from).filter(entry=>entry.to===to&&!entry.polarity).map(entry=>entry.tag)),subset=(left,right)=>[...left].every(tag=>right.has(tag));profiles.set(data.origin,{origin:data.origin,fromMajor:data.fromMajor,toMajor:data.toMajor,aliasRemovalMajor:data.aliasRemovalMajor,data,skipped,tags,isEmpty:tags.size===0&&data.moduleReviews.length===0,renameFor:(tag,kind,name)=>renamesByOwner.get(ownerKey(tag,kind,name))??null,renamesNamed,renamesOnto:(kind,name)=>renamesByTarget.get(nameKey(kind,name))??[],reviewFor:(tag,kind,name)=>reviewsByOwner.get(ownerKey(tag,kind,name))??null,reviewsNamed:(kind,name)=>reviewsByName.get(nameKey(kind,name))??[],detailFor:(tag,event)=>detailsByOwner.get(ownerKey(tag,"event",event))??null,detailsNamed:event=>detailsByName.get(event)??[],detailFieldFor:(tag,event)=>fieldsByOwnerEvent.get(ownerKey(tag,"event",event))??null,detailFieldsNamed:event=>fieldsByEvent.get(event)??[],retiredEventFor:(tag,event)=>retiredByOwner.get(ownerKey(tag,"event",event))??null,retiredEventsNamed:event=>retiredByName.get(event)??[],propertyChangeFor:(tag,property)=>propertiesByOwner.get(ownerKey(tag,"property",property))??null,defaultsFor:tag=>defaultsByTag.get(tag)??[],preservedDefaultsFor:tag=>allDefaults.filter(entry=>entry.tag===tag),defaultAliasesFor:(tag,attribute)=>{const aliases=new Set([attribute]);for(const rename of allRenames)rename.tag!==tag||rename.kind!=="attribute"||(rename.from===attribute&&aliases.add(rename.to),rename.to===attribute&&aliases.add(rename.from));return[...aliases]},slotContentFor:tag=>slotContentByTag.get(tag)??[],exposes:(kind,name,tag)=>!!tag&&exposersOf(kind,name).has(tag),exposers:(kind,name)=>[...exposersOf(kind,name)],targetKeepers(kind,from,to){const moving=movers(kind,from,to);return[...exposersOf(kind,to)].filter(tag=>!moving.has(tag))},sourceKeepers(kind,from,to){const moving=movers(kind,from,to);return[...exposersOf(kind,from)].filter(tag=>!moving.has(tag))},isGlobal(kind,from){const entries=renamesNamed(kind,from);if(!entries.length||entries.some(entry=>entry.polarity))return!1;const targets=new Set(entries.map(entry=>entry.to));if(targets.size!==1)return!1;const[to]=targets,moving=movers(kind,from,to);return exposersOf(kind,from).size>0&&subset(exposersOf(kind,from),moving)&&subset(exposersOf(kind,to),moving)},gainedOwners(kind,name){const onto=(renamesByTarget.get(nameKey(kind,name))??[]).filter(entry=>!entry.polarity);if(!onto.length)return[];const gained=new Set(onto.map(entry=>entry.tag));return[...exposersOf(kind,name)].filter(tag=>!gained.has(tag)).length?[...gained].sort(compareText):[]}})}return profiles}export{LYRA_RENAME_ORIGINS,compareVersions,createRenameProfiles,emptyRenameLedger,emptyRenameProjection,mirroredMembers,projectRenameLedger,validateRenameLedger,validateRenameLedgerShape};