diff --git a/.gitignore b/.gitignore index 438d125b47a0..d3e5648a345d 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ packages/react-devtools-inline/dist packages/react-devtools-shell/dist packages/react-devtools-timeline/dist +scripts/release-notes/node_modules +scripts/release-notes/state.json diff --git a/scripts/release-notes/.eslintrc.cjs b/scripts/release-notes/.eslintrc.cjs new file mode 100644 index 000000000000..3d39d77f4699 --- /dev/null +++ b/scripts/release-notes/.eslintrc.cjs @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = { + parserOptions: { + sourceType: 'module', + }, + rules: { + strict: 'off', + }, +}; diff --git a/scripts/release-notes/.gitignore b/scripts/release-notes/.gitignore new file mode 100644 index 000000000000..8fce603003c1 --- /dev/null +++ b/scripts/release-notes/.gitignore @@ -0,0 +1 @@ +data/ diff --git a/scripts/release-notes/README.md b/scripts/release-notes/README.md new file mode 100644 index 000000000000..af157bbd409a --- /dev/null +++ b/scripts/release-notes/README.md @@ -0,0 +1,41 @@ +# Release Notes Generator + +Tool for generating release notes. + +## Setup + +```sh +cd scripts/release-notes +yarn install +``` + +## Usage + +### 1. Generate commit data + +```sh +yarn gen-data -v +``` + +This exports all commits since the given git tag to `data/commits.json`. It also resolves GitHub usernames for each commit author via the GitHub API (requires `gh` CLI to be authenticated). + +Example: +```sh +yarn gen-data -v 19.1.0 +``` + +### 2. Run the app + +```sh +yarn dev +``` + +### 3. Triage commits + +- **Include/Reviewed checkboxes** — mark commits to include in the release notes or mark as reviewed (reviewed-only commits fade out) +- **Tags** — assign custom tags to group related commits together +- **Filters** — filter the table by text search, reviewed status, category, or tag + +## State + +Triage state (selections, tags, assignments) is saved to `state.json` automatically. This is gitignored. Regenerating commit data does not affect saved state. diff --git a/scripts/release-notes/gen-data.mjs b/scripts/release-notes/gen-data.mjs new file mode 100644 index 000000000000..6fbb3834a0e9 --- /dev/null +++ b/scripts/release-notes/gen-data.mjs @@ -0,0 +1,144 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {execSync} from 'child_process'; +import {writeFileSync, mkdirSync} from 'fs'; +import {join, dirname} from 'path'; +import {fileURLToPath} from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const OUTPUT_DIR = join(__dirname, 'data'); +const OUTPUT_FILE = join(OUTPUT_DIR, 'commits.json'); + +function parseArgs() { + const args = process.argv.slice(2); + const vIndex = args.indexOf('-v'); + if (vIndex === -1 || vIndex + 1 >= args.length) { + console.error('Usage: node gen-data.mjs -v '); + console.error('Example: node gen-data.mjs -v 19.2.0'); + process.exit(1); + } + return args[vIndex + 1]; +} + +function resolveTag(version) { + const tag = version.startsWith('v') ? version : `v${version}`; + try { + execSync(`git tag -l "${tag}" | grep -q .`, {stdio: 'pipe'}); + } catch { + console.error(`Error: git tag "${tag}" not found.`); + console.error( + 'Available recent tags:', + execSync('git tag --sort=-creatordate | head -5').toString().trim() + ); + process.exit(1); + } + return tag; +} + +function resolveGitHubUsernames(commits, repo) { + // Dedupe by author name — only need one commit per unique author + const authorToHash = new Map(); + for (const commit of commits) { + if (!authorToHash.has(commit.author)) { + authorToHash.set(commit.author, commit.fullHash); + } + } + + const authorToUsername = new Map(); + const entries = Array.from(authorToHash.entries()); + console.log(`Resolving GitHub usernames for ${entries.length} unique authors...`); + + for (const [author, hash] of entries) { + try { + const login = execSync( + `gh api repos/${repo}/commits/${hash} --jq '.author.login'`, + {stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000} + ) + .toString() + .trim(); + if (login && login !== 'null') { + authorToUsername.set(author, login); + } + } catch { + // Silently skip — will fall back to display name + } + } + + console.log(`Resolved ${authorToUsername.size}/${entries.length} usernames.`); + return authorToUsername; +} + +function getCommits(lastRelease) { + const listOfCommits = execSync( + `git log --pretty=format:"%h|%ai|%aN|%ae" ${lastRelease}...` + ).toString(); + + const summary = execSync( + `git log --pretty=format:"%s" ${lastRelease}...` + ) + .toString() + .split('\n'); + + const body = execSync( + `git log --pretty=format:"%b" ${lastRelease}...` + ) + .toString() + .split('\n'); + + const commits = listOfCommits.split('\n').map((commitMessage, index) => { + const diffMatch = body[index]?.match(/D\d+/); + const diff = diffMatch != null && diffMatch[0]; + const [hash, date, name] = commitMessage.split('|'); + return { + hash: hash.slice(0, 7), + fullHash: hash, + summary: summary[index], + message: body[index], + author: name, + diff, + date, + }; + }); + + return commits; +} + +// Detect the GitHub repo from the git remote +function getRepo() { + try { + const remote = execSync('git remote get-url origin', {stdio: 'pipe'}) + .toString() + .trim(); + const match = remote.match(/github\.com[:/](.+?)(?:\.git)?$/); + if (match) return match[1]; + } catch { + // fall through + } + return 'facebook/react'; +} + +const version = parseArgs(); +const lastRelease = resolveTag(version); +const commits = getCommits(lastRelease); +const repo = getRepo(); +const usernameMap = resolveGitHubUsernames(commits, repo); + +// Attach github username to each commit +for (const commit of commits) { + const username = usernameMap.get(commit.author); + if (username) { + commit.github = username; + } +} + +mkdirSync(OUTPUT_DIR, {recursive: true}); +writeFileSync(OUTPUT_FILE, JSON.stringify({lastRelease, commits}, null, 2)); + +console.log( + `Wrote ${commits.length} commits (since ${lastRelease}) to data/commits.json` +); diff --git a/scripts/release-notes/index.html b/scripts/release-notes/index.html new file mode 100644 index 000000000000..63f23be1f91d --- /dev/null +++ b/scripts/release-notes/index.html @@ -0,0 +1,12 @@ + + + + + + React Release Notes + + +
+ + + diff --git a/scripts/release-notes/package.json b/scripts/release-notes/package.json new file mode 100644 index 000000000000..e52a52390577 --- /dev/null +++ b/scripts/release-notes/package.json @@ -0,0 +1,19 @@ +{ + "name": "release-notes", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "gen-data": "node gen-data.mjs" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "@tanstack/react-table": "^8.20.0" + }, + "devDependencies": { + "vite": "^6.0.0", + "@vitejs/plugin-react": "^4.3.0" + } +} diff --git a/scripts/release-notes/plugins/api.js b/scripts/release-notes/plugins/api.js new file mode 100644 index 000000000000..29f966c43769 --- /dev/null +++ b/scripts/release-notes/plugins/api.js @@ -0,0 +1,72 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {readFileSync, writeFileSync, existsSync} from 'fs'; +import {join} from 'path'; + +const STATE_FILE = join(process.cwd(), 'data', 'state.json'); + +function getDefaultState() { + return { + includedCommits: {}, + reviewedCommits: {}, + customTags: [], + tagAssignments: {}, + }; +} + +function readState() { + if (existsSync(STATE_FILE)) { + try { + return JSON.parse(readFileSync(STATE_FILE, 'utf-8')); + } catch { + return getDefaultState(); + } + } + return getDefaultState(); +} + +function writeState(state) { + writeFileSync(STATE_FILE, JSON.stringify(state, null, 2)); +} + +export default function apiPlugin() { + return { + name: 'release-notes-api', + configureServer(server) { + server.middlewares.use((req, res, next) => { + if (req.url === '/api/state' && req.method === 'GET') { + const state = readState(); + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify(state)); + return; + } + + if (req.url === '/api/state' && req.method === 'POST') { + let body = ''; + req.on('data', chunk => { + body += chunk; + }); + req.on('end', () => { + try { + const state = JSON.parse(body); + writeState(state); + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({ok: true})); + } catch { + res.statusCode = 400; + res.end(JSON.stringify({error: 'Invalid JSON'})); + } + }); + return; + } + + next(); + }); + }, + }; +} diff --git a/scripts/release-notes/src/App.jsx b/scripts/release-notes/src/App.jsx new file mode 100644 index 000000000000..b8ad60a503d9 --- /dev/null +++ b/scripts/release-notes/src/App.jsx @@ -0,0 +1,129 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {useCallback, useEffect, useState} from 'react'; +import {fetchState, saveState} from './api'; +import commitData from '../data/commits.json'; +import CommitTable from './components/CommitTable'; +import TagManager from './components/TagManager'; +import ReleaseNotes from './components/ReleaseNotes'; +import stateData from '../data/state.json'; + +const DEFAULT_STATE = { + includedCommits: {}, + reviewedCommits: {}, + customTags: [], + tagAssignments: {}, +}; + +export {DEFAULT_STATE}; + +export default function App() { + const {commits, lastRelease} = commitData; + const [state, setState] = useState(() => ({...DEFAULT_STATE, ...stateData})); + const [showTagManager, setShowTagManager] = useState(false); + const [showSidebar, setShowSidebar] = useState(true); + + const updateState = newState => { + setState(newState); + saveState(newState); + }; + + const handleTagsChange = useCallback( + newTags => { + if (!state) return; + const tagIds = new Set(newTags.map(t => t.id)); + const cleanedAssignments = {}; + for (const [hash, ids] of Object.entries(state.tagAssignments)) { + const filtered = ids.filter(id => tagIds.has(id)); + if (filtered.length > 0) { + cleanedAssignments[hash] = filtered; + } + } + updateState({ + ...state, + customTags: newTags, + tagAssignments: cleanedAssignments, + }); + }, + [state, updateState] + ); + + useEffect(() => { + if (!showTagManager) return; + const handleEscape = e => { + if (e.key === 'Escape') setShowTagManager(false); + }; + document.addEventListener('keydown', handleEscape); + return () => document.removeEventListener('keydown', handleEscape); + }, [showTagManager]); + + const unreviewed = commits.filter(c => !state.reviewedCommits[c.hash]).length; + + return ( +
+
+

+ React commits since {lastRelease} ({commits.length} commits,{' '} + {unreviewed} unreviewed) +

+
+ + +
+
+
+
+ +
+ {showSidebar && ( +
+ +
+ )} +
+ {showTagManager && ( +
{ + if (e.target === e.currentTarget) setShowTagManager(false); + }}> +
+
+

Manage Tags

+ +
+ +
+
+ )} +
+ ); +} diff --git a/scripts/release-notes/src/api.js b/scripts/release-notes/src/api.js new file mode 100644 index 000000000000..724c4a0ad8b3 --- /dev/null +++ b/scripts/release-notes/src/api.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +export async function fetchState() { + const res = await fetch('/api/state'); + return res.json(); +} + +export async function saveState(state) { + await fetch('/api/state', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(state), + }); +} diff --git a/scripts/release-notes/src/components/CommitTable.jsx b/scripts/release-notes/src/components/CommitTable.jsx new file mode 100644 index 000000000000..f36eb0d9a5cb --- /dev/null +++ b/scripts/release-notes/src/components/CommitTable.jsx @@ -0,0 +1,253 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {useMemo, useState} from 'react'; +import { + useReactTable, + getCoreRowModel, + getSortedRowModel, + getFilteredRowModel, + flexRender, +} from '@tanstack/react-table'; +import TagPicker from './TagPicker'; + +const REPO_URL = 'https://github.com/facebook/react'; + +export default function CommitTable({commits, state, onStateChange}) { + const [sorting, setSorting] = useState([]); + const [globalFilter, setGlobalFilter] = useState(''); + const [hideReviewed, setHideReviewed] = useState(false); + const [tagFilter, setTagFilter] = useState('all'); + + const filteredCommits = useMemo(() => { + return commits.filter(commit => { + // Reviewed filter + if (hideReviewed && state.reviewedCommits[commit.hash]) return false; + + // Tag filter + const commitTagIds = state.tagAssignments[commit.hash] || []; + if (tagFilter === 'untagged' && commitTagIds.length > 0) return false; + if ( + tagFilter !== 'all' && + tagFilter !== 'untagged' && + !commitTagIds.includes(tagFilter) + ) + return false; + + return true; + }); + }, [commits, state, hideReviewed, tagFilter]); + + const columns = useMemo( + () => [ + { + id: 'include', + header: 'Include', + cell: ({row}) => { + const hash = row.original.hash; + const isIncluded = !!state.includedCommits[hash]; + return ( + { + const newIncluded = {...state.includedCommits}; + const newReviewed = {...state.reviewedCommits}; + if (isIncluded) { + delete newIncluded[hash]; + } else { + newIncluded[hash] = true; + newReviewed[hash] = true; + } + onStateChange({ + ...state, + includedCommits: newIncluded, + reviewedCommits: newReviewed, + }); + }} + /> + ); + }, + size: 40, + enableSorting: false, + }, + { + id: 'reviewed', + header: 'Reviewed', + cell: ({row}) => { + const hash = row.original.hash; + const isReviewed = !!state.reviewedCommits[hash]; + return ( + { + const newReviewed = {...state.reviewedCommits}; + const newIncluded = {...state.includedCommits}; + if (isReviewed) { + delete newReviewed[hash]; + delete newIncluded[hash]; + } else { + newReviewed[hash] = true; + } + onStateChange({ + ...state, + includedCommits: newIncluded, + reviewedCommits: newReviewed, + }); + }} + /> + ); + }, + size: 40, + enableSorting: false, + }, + { + accessorKey: 'summary', + header: 'Summary', + cell: ({row}) => ( + + {row.original.summary} + + ), + size: 400, + }, + { + accessorKey: 'author', + header: 'Author', + size: 120, + }, + { + id: 'tags', + header: 'Tags', + cell: ({row}) => ( + { + onStateChange({ + ...state, + tagAssignments: { + ...state.tagAssignments, + [row.original.hash]: tagIds, + }, + }); + }} + /> + ), + size: 200, + enableSorting: false, + }, + ], + [state, onStateChange] + ); + + const hasActiveFilters = hideReviewed || tagFilter !== 'all'; + + const clearFilters = () => { + setHideReviewed(false); + setTagFilter('all'); + }; + + const table = useReactTable({ + data: filteredCommits, + columns, + state: {sorting, globalFilter}, + onSortingChange: setSorting, + onGlobalFilterChange: setGlobalFilter, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + }); + + return ( +
+
+ setGlobalFilter(e.target.value)} + className="filter-input" + /> + + + {hasActiveFilters && ( + + )} + + {filteredCommits.length}/{commits.length} + +
+ + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => ( + + ))} + + ))} + + + {table.getRowModel().rows.map(row => { + const hash = row.original.hash; + const isReviewed = !!state.reviewedCommits[hash]; + const isIncluded = !!state.includedCommits[hash]; + const rowClass = isReviewed && !isIncluded ? 'reviewed-row' : ''; + return ( + + {row.getVisibleCells().map(cell => ( + + ))} + + ); + })} + +
+ {flexRender( + header.column.columnDef.header, + header.getContext() + )} + {{asc: ' ^', desc: ' v'}[header.column.getIsSorted()] ?? ''} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+ ); +} diff --git a/scripts/release-notes/src/components/ReleaseNotes.jsx b/scripts/release-notes/src/components/ReleaseNotes.jsx new file mode 100644 index 000000000000..15ffd82547bc --- /dev/null +++ b/scripts/release-notes/src/components/ReleaseNotes.jsx @@ -0,0 +1,189 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {useState} from 'react'; + +const GITHUB_URL = 'https://github.com'; +const REPO_URL = `${GITHUB_URL}/facebook/react`; + +function extractPR(summary) { + const match = summary.match(/#(\d+)/); + return match ? match[1] : null; +} + +function cleanSummary(summary) { + // Strip trailing PR reference like "(#12345)" since the suffix handles it + return summary.replace(/\s*\(#\d+\)\s*$/, ''); +} + +function authorRef(commit) { + if (commit.github) { + return `[@${commit.github}](${GITHUB_URL}/${commit.github})`; + } + return `@${commit.author}`; +} + +function commitSuffix(commit) { + const pr = extractPR(commit.summary); + if (pr) { + return `(${authorRef(commit)} [#${pr}](${REPO_URL}/pull/${pr}))`; + } + return `(${authorRef(commit)} [${commit.hash}](${REPO_URL}/commit/${commit.fullHash}))`; +} + +function groupSuffix(tagCommits) { + const authors = new Map(); + const refs = []; + tagCommits.forEach(commit => { + const ref = authorRef(commit); + const key = commit.github || commit.author; + if (!authors.has(key)) { + authors.set(key, ref); + } + const pr = extractPR(commit.summary); + if (pr) { + refs.push(`[#${pr}](${REPO_URL}/pull/${pr})`); + } else { + refs.push(`[${commit.hash}](${REPO_URL}/commit/${commit.fullHash})`); + } + }); + const authorList = Array.from(authors.values()).join(', '); + const refList = refs.join(', '); + return `(${authorList}: ${refList})`; +} + +function escapeHtml(str) { + return str.replace(/&/g, '&').replace(//g, '>'); +} + +function markdownToHtml(md) { + return md + .split('\n') + .map(line => { + // Headings + if (line.startsWith('### ')) + return `

${escapeHtml(line.slice(4))}

`; + if (line.startsWith('## ')) + return `

${escapeHtml(line.slice(3))}

`; + // List items + if (line.startsWith('- ')) { + let content = escapeHtml(line.slice(2)); + // Links: [text](url) + content = content.replace( + /\[([^\]]+)\]\(([^)]+)\)/g, + '$1' + ); + // Bold + content = content.replace(/\*\*([^*]+)\*\*/g, '$1'); + // Inline code + content = content.replace(/`([^`]+)`/g, '$1'); + return `
  • ${content}
  • `; + } + if (line.trim() === '') return ''; + // Links and bold in plain text + let content = escapeHtml(line); + content = content.replace( + /\[([^\]]+)\]\(([^)]+)\)/g, + '$1' + ); + content = content.replace(/\*\*([^*]+)\*\*/g, '$1'); + // Inline code + content = content.replace(/`([^`]+)`/g, '$1'); + return `

    ${content}

    `; + }) + .join('\n'); +} + +export default function ReleaseNotes({commits, state, lastRelease}) { + const [viewMode, setViewMode] = useState('raw'); + + const {includedCommits, customTags, tagAssignments} = state; + + // Build tag groups + const tagGroups = new Map(); + customTags.forEach(t => tagGroups.set(t.id, {tag: t, commits: []})); + + const untagged = []; + + commits.forEach(commit => { + if (!includedCommits[commit.hash]) return; + + const commitTagIds = tagAssignments[commit.hash] || []; + const firstTagId = commitTagIds.find(id => tagGroups.has(id)); + if (firstTagId) { + tagGroups.get(firstTagId).commits.push(commit); + } else { + untagged.push(commit); + } + }); + + // Generate markdown + const lines = []; + lines.push(`## React ${lastRelease}\n`); + + // Tag sections + const activeTags = Array.from(tagGroups.values()).filter( + g => g.commits.length > 0 + ); + activeTags.forEach(({tag, commits: tagCommits}) => { + lines.push(`### ${tag.name}\n`); + if (tag.isFeature) { + lines.push( + `- [${tag.name}] ${groupSuffix(tagCommits)}` + ); + } else { + tagCommits.forEach(commit => { + lines.push(`- ${cleanSummary(commit.summary)} ${commitSuffix(commit)}`); + }); + } + lines.push(''); + }); + + // Untagged + if (untagged.length > 0) { + lines.push(`### Other changes\n`); + untagged.forEach(commit => { + lines.push(`- ${cleanSummary(commit.summary)} ${commitSuffix(commit)}`); + }); + } + + const markdown = lines.join('\n'); + + const copyToClipboard = () => { + navigator.clipboard.writeText(markdown); + }; + + return ( +
    +
    + +
    + + +
    +
    + {viewMode === 'raw' ? ( +
    {markdown}
    + ) : ( +
    + )} +
    + ); +} diff --git a/scripts/release-notes/src/components/TagManager.jsx b/scripts/release-notes/src/components/TagManager.jsx new file mode 100644 index 000000000000..f159d7a72995 --- /dev/null +++ b/scripts/release-notes/src/components/TagManager.jsx @@ -0,0 +1,107 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {useState} from 'react'; + +export default function TagManager({tags, onTagsChange}) { + const [newName, setNewName] = useState(''); + const [newColor, setNewColor] = useState('#8b5cf6'); + const [editingId, setEditingId] = useState(null); + const [editName, setEditName] = useState(''); + + const addTag = () => { + if (!newName.trim()) return; + const id = 'tag_' + Date.now(); + onTagsChange([ + ...tags, + {id, name: newName.trim(), color: newColor, isFeature: false}, + ]); + setNewName(''); + }; + + const deleteTag = id => { + onTagsChange(tags.filter(t => t.id !== id)); + }; + + const startEdit = tag => { + setEditingId(tag.id); + setEditName(tag.name); + }; + + const saveEdit = id => { + onTagsChange( + tags.map(t => (t.id === id ? {...t, name: editName.trim()} : t)) + ); + setEditingId(null); + }; + + const changeColor = (id, color) => { + onTagsChange(tags.map(t => (t.id === id ? {...t, color} : t))); + }; + + const toggleFeature = id => { + onTagsChange( + tags.map(t => (t.id === id ? {...t, isFeature: !t.isFeature} : t)) + ); + }; + + return ( +
    + {tags.map(tag => ( +
    + changeColor(tag.id, e.target.value)} + /> + {editingId === tag.id ? ( + setEditName(e.target.value)} + onBlur={() => saveEdit(tag.id)} + onKeyDown={e => e.key === 'Enter' && saveEdit(tag.id)} + autoFocus + /> + ) : ( + startEdit(tag)} style={{cursor: 'pointer'}}> + {tag.name} + + )} + + +
    + ))} +
    + setNewColor(e.target.value)} + /> + setNewName(e.target.value)} + onKeyDown={e => e.key === 'Enter' && addTag()} + /> + +
    +
    + ); +} diff --git a/scripts/release-notes/src/components/TagPicker.jsx b/scripts/release-notes/src/components/TagPicker.jsx new file mode 100644 index 000000000000..5eafb5a9d999 --- /dev/null +++ b/scripts/release-notes/src/components/TagPicker.jsx @@ -0,0 +1,32 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +export default function TagPicker({tags, selectedTagIds, onChange}) { + const toggle = tagId => { + if (selectedTagIds.includes(tagId)) { + onChange(selectedTagIds.filter(id => id !== tagId)); + } else { + onChange([...selectedTagIds, tagId]); + } + }; + + if (tags.length === 0) return null; + + return ( +
    + {tags.map(tag => ( + + ))} +
    + ); +} diff --git a/scripts/release-notes/src/main.jsx b/scripts/release-notes/src/main.jsx new file mode 100644 index 000000000000..8a0004ad879a --- /dev/null +++ b/scripts/release-notes/src/main.jsx @@ -0,0 +1,13 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {createRoot} from 'react-dom/client'; +import App from './App'; +import './style.css'; + +const root = createRoot(document.getElementById('app')); +root.render(); diff --git a/scripts/release-notes/src/style.css b/scripts/release-notes/src/style.css new file mode 100644 index 000000000000..b07e77e4b53f --- /dev/null +++ b/scripts/release-notes/src/style.css @@ -0,0 +1,371 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + margin: 0; + padding: 16px 20px; + background: #fafafa; +} + +h1 { + font-size: 1.5rem; + font-weight: 500; + margin: 0; +} + +/* App header */ +.app-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 12px; + flex-wrap: wrap; +} +.header-controls { + display: flex; + align-items: center; + gap: 8px; +} + +/* Layout */ +.layout { + display: grid; + grid-template-columns: 65% 35%; + gap: 16px; +} +.layout-full { + grid-template-columns: 1fr; +} + +/* Table */ +.table-container { + overflow-x: auto; +} + +.table-toolbar { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 0; + margin-bottom: 8px; + flex-wrap: wrap; +} + +.filter-input { + padding: 6px 10px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 14px; + width: 250px; +} + +.filter-select { + padding: 6px 8px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 13px; + background: white; + cursor: pointer; +} + +.filter-count { + font-size: 12px; + color: #6b7280; + white-space: nowrap; +} + +.commit-table { + width: 100%; + border-collapse: collapse; + font-size: 13px; +} + +.commit-table th { + text-align: left; + padding: 8px 6px; + border-bottom: 2px solid #e5e7eb; + background: #f8f9fa; + white-space: nowrap; + user-select: none; +} + +.commit-table td { + padding: 6px; + border-bottom: 1px solid #f0f0f0; + vertical-align: middle; +} + +/* Center-align checkbox columns */ +.commit-table th:nth-child(1), +.commit-table th:nth-child(2), +.commit-table td:nth-child(1), +.commit-table td:nth-child(2) { + text-align: center; +} + +.commit-table a { + color: #0969da; + text-decoration: none; +} +.commit-table a:hover { + text-decoration: underline; +} + +.reviewed-row { + opacity: 0.35; +} + +/* Row checkboxes */ +.row-checkbox { + width: 16px; + height: 16px; + cursor: pointer; + accent-color: #0969da; +} + +.filter-checkbox { + display: flex; + align-items: center; + gap: 4px; + font-size: 13px; + cursor: pointer; + white-space: nowrap; +} + +/* Toolbar buttons */ +.toolbar-btn { + padding: 6px 12px; + border: 1px solid #ccc; + border-radius: 4px; + background: white; + cursor: pointer; + font-size: 13px; +} +.toolbar-btn:hover { + background: #f0f0f0; +} + +.sidebar-toggle-btn { + padding: 6px 12px; + border: 1px solid #ccc; + border-radius: 4px; + background: white; + cursor: pointer; + font-size: 13px; +} +.sidebar-toggle-btn:hover { + background: #f0f0f0; +} + +/* Modal */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.4); + display: flex; + align-items: center; + justify-content: center; + z-index: 100; +} +.modal-content { + background: white; + border-radius: 8px; + padding: 20px; + width: 450px; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); +} +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} +.modal-header h3 { + margin: 0; + font-size: 16px; +} +.modal-close-btn { + background: none; + border: none; + font-size: 22px; + cursor: pointer; + padding: 2px 8px; + border-radius: 4px; + color: #666; + line-height: 1; +} +.modal-close-btn:hover { + background: #f0f0f0; + color: #333; +} + +/* Release notes sidebar */ +.release-notes-panel { + position: sticky; + top: 16px; + max-height: calc(100vh - 32px); + overflow-y: auto; + background: white; + border: 1px solid #e5e7eb; + border-radius: 6px; + padding: 16px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06); +} +.release-notes-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; +} +.release-notes-panel pre { + white-space: pre-wrap; + font-family: 'SF Mono', 'Fira Code', monospace; + font-size: 12px; + line-height: 1.5; +} +.copy-btn { + padding: 4px 10px; + font-size: 12px; + border: 1px solid #ccc; + border-radius: 4px; + background: white; + cursor: pointer; +} +.copy-btn:hover { + background: #f0f0f0; +} + +/* View toggle (raw/preview) */ +.view-toggle { + display: flex; + gap: 0; +} +.view-toggle button { + padding: 3px 10px; + border: 1px solid #ccc; + background: white; + cursor: pointer; + font-size: 12px; +} +.view-toggle button:first-child { + border-radius: 3px 0 0 3px; +} +.view-toggle button:last-child { + border-radius: 0 3px 3px 0; + border-left: none; +} +.view-toggle button.active { + background: #0969da; + color: white; + border-color: #0969da; +} + +/* Markdown preview */ +.markdown-preview { + font-size: 13px; + line-height: 1.6; +} +.markdown-preview h2 { + font-size: 1.2em; + margin: 16px 0 8px 0; + padding-bottom: 4px; + border-bottom: 1px solid #eee; +} +.markdown-preview h3 { + font-size: 1em; + margin: 12px 0 6px 0; +} +.markdown-preview li { + margin-bottom: 4px; + list-style-type: disc; + margin-left: 20px; +} +.markdown-preview a { + color: #0969da; + text-decoration: none; +} +.markdown-preview a:hover { + text-decoration: underline; +} +.markdown-preview code { + background: #f0f2f5; + padding: 1px 5px; + border-radius: 3px; + font-family: 'SF Mono', 'Fira Code', monospace; + font-size: 0.9em; +} + +/* Manager panels */ +.manager-row { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 6px; +} +.manager-row input[type="text"] { + padding: 4px 8px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 14px; +} +.manager-row input[type="color"] { + width: 28px; + height: 28px; + border: none; + cursor: pointer; +} +.feature-checkbox { + display: flex; + align-items: center; + gap: 4px; + font-size: 13px; + color: #666; + white-space: nowrap; +} +.feature-checkbox input[type="checkbox"] { + margin: 0; +} +.delete-btn { + background: none; + border: none; + color: #cf222e; + cursor: pointer; + font-size: 16px; + padding: 2px 6px; +} +.delete-btn:hover { + background: #fee; + border-radius: 4px; +} + +/* Tag picker (in-row) */ +.tag-picker { + display: flex; + gap: 3px; + flex-wrap: wrap; +} +.tag-btn { + padding: 3px 8px; + font-size: 11px; + font-weight: 500; + line-height: 1.3; + border-radius: 4px; + cursor: pointer; + border: 1px solid rgba(0, 0, 0, 0.15); + opacity: 0.5; + color: black; + transition: opacity 0.1s; +} +.tag-btn.active { + opacity: 1; + font-weight: 600; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3); +} diff --git a/scripts/release-notes/vite.config.js b/scripts/release-notes/vite.config.js new file mode 100644 index 000000000000..cb9ffe5107b6 --- /dev/null +++ b/scripts/release-notes/vite.config.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {defineConfig} from 'vite'; +import react from '@vitejs/plugin-react'; +import apiPlugin from './plugins/api.js'; + +export default defineConfig({ + plugins: [react(), apiPlugin()], + server: { + port: 3123, + watch: { + ignored: ['**/state.json'], + }, + }, +}); diff --git a/scripts/release-notes/yarn.lock b/scripts/release-notes/yarn.lock new file mode 100644 index 000000000000..c560456b2748 --- /dev/null +++ b/scripts/release-notes/yarn.lock @@ -0,0 +1,776 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== + dependencies: + "@babel/helper-validator-identifier" "^7.28.5" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/compat-data@^7.28.6": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" + integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== + +"@babel/core@^7.28.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" + integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helpers" "^7.28.6" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/remapping" "^2.3.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.29.0": + version "7.29.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== + dependencies: + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + +"@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== + dependencies: + "@babel/compat-data" "^7.28.6" + "@babel/helper-validator-option" "^7.27.1" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-globals@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== + +"@babel/helper-module-imports@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== + dependencies: + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" + +"@babel/helper-module-transforms@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" + integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== + dependencies: + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" + +"@babel/helper-plugin-utils@^7.27.1": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" + integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + +"@babel/helper-validator-option@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== + +"@babel/helpers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7" + integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw== + dependencies: + "@babel/template" "^7.28.6" + "@babel/types" "^7.28.6" + +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6" + integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww== + dependencies: + "@babel/types" "^7.29.0" + +"@babel/plugin-transform-react-jsx-self@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz#af678d8506acf52c577cac73ff7fe6615c85fc92" + integrity sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/plugin-transform-react-jsx-source@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz#dcfe2c24094bb757bf73960374e7c55e434f19f0" + integrity sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" + +"@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" + debug "^4.3.1" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.28.6", "@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + +"@esbuild/aix-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c" + integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA== + +"@esbuild/android-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752" + integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg== + +"@esbuild/android-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a" + integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg== + +"@esbuild/android-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16" + integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg== + +"@esbuild/darwin-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd" + integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== + +"@esbuild/darwin-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e" + integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA== + +"@esbuild/freebsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe" + integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg== + +"@esbuild/freebsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3" + integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ== + +"@esbuild/linux-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977" + integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ== + +"@esbuild/linux-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9" + integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw== + +"@esbuild/linux-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0" + integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA== + +"@esbuild/linux-loong64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0" + integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng== + +"@esbuild/linux-mips64el@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd" + integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw== + +"@esbuild/linux-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869" + integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA== + +"@esbuild/linux-riscv64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6" + integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w== + +"@esbuild/linux-s390x@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663" + integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg== + +"@esbuild/linux-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306" + integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== + +"@esbuild/netbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4" + integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg== + +"@esbuild/netbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076" + integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ== + +"@esbuild/openbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd" + integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A== + +"@esbuild/openbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679" + integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw== + +"@esbuild/openharmony-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d" + integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg== + +"@esbuild/sunos-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6" + integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w== + +"@esbuild/win32-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323" + integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg== + +"@esbuild/win32-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267" + integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ== + +"@esbuild/win32-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5" + integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA== + +"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@rolldown/pluginutils@1.0.0-beta.27": + version "1.0.0-beta.27" + resolved "https://registry.yarnpkg.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz#47d2bf4cef6d470b22f5831b420f8964e0bf755f" + integrity sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA== + +"@rollup/rollup-android-arm-eabi@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz#a6742c74c7d9d6d604ef8a48f99326b4ecda3d82" + integrity sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg== + +"@rollup/rollup-android-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz#97247be098de4df0c11971089fd2edf80a5da8cf" + integrity sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q== + +"@rollup/rollup-darwin-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz#674852cf14cf11b8056e0b1a2f4e872b523576cf" + integrity sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg== + +"@rollup/rollup-darwin-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz#36dfd7ed0aaf4d9d89d9ef983af72632455b0246" + integrity sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w== + +"@rollup/rollup-freebsd-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz#2f87c2074b4220260fdb52a9996246edfc633c22" + integrity sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA== + +"@rollup/rollup-freebsd-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz#9b5a26522a38a95dc06616d1939d4d9a76937803" + integrity sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg== + +"@rollup/rollup-linux-arm-gnueabihf@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz#86aa4859385a8734235b5e40a48e52d770758c3a" + integrity sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw== + +"@rollup/rollup-linux-arm-musleabihf@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz#cbe70e56e6ece8dac83eb773b624fc9e5a460976" + integrity sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA== + +"@rollup/rollup-linux-arm64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz#d14992a2e653bc3263d284bc6579b7a2890e1c45" + integrity sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA== + +"@rollup/rollup-linux-arm64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz#2fdd1ddc434ea90aeaa0851d2044789b4d07f6da" + integrity sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA== + +"@rollup/rollup-linux-loong64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz#8a181e6f89f969f21666a743cd411416c80099e7" + integrity sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg== + +"@rollup/rollup-linux-loong64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz#904125af2babc395f8061daa27b5af1f4e3f2f78" + integrity sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q== + +"@rollup/rollup-linux-ppc64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz#a57970ac6864c9a3447411a658224bdcf948be22" + integrity sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA== + +"@rollup/rollup-linux-ppc64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz#bb84de5b26870567a4267666e08891e80bb56a63" + integrity sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA== + +"@rollup/rollup-linux-riscv64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz#72d00d2c7fb375ce3564e759db33f17a35bffab9" + integrity sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg== + +"@rollup/rollup-linux-riscv64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz#4c166ef58e718f9245bd31873384ba15a5c1a883" + integrity sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg== + +"@rollup/rollup-linux-s390x-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz#bb5025cde9a61db478c2ca7215808ad3bce73a09" + integrity sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w== + +"@rollup/rollup-linux-x64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz#9b66b1f9cd95c6624c788f021c756269ffed1552" + integrity sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg== + +"@rollup/rollup-linux-x64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz#b007ca255dc7166017d57d7d2451963f0bd23fd9" + integrity sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg== + +"@rollup/rollup-openbsd-x64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz#e8b357b2d1aa2c8d76a98f5f0d889eabe93f4ef9" + integrity sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ== + +"@rollup/rollup-openharmony-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz#96c2e3f4aacd3d921981329831ff8dde492204dc" + integrity sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA== + +"@rollup/rollup-win32-arm64-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz#2d865149d706d938df8b4b8f117e69a77646d581" + integrity sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A== + +"@rollup/rollup-win32-ia32-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz#abe1593be0fa92325e9971c8da429c5e05b92c36" + integrity sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA== + +"@rollup/rollup-win32-x64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz#c4af3e9518c9a5cd4b1c163dc81d0ad4d82e7eab" + integrity sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA== + +"@rollup/rollup-win32-x64-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz#4584a8a87b29188a4c1fe987a9fcf701e256d86c" + integrity sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA== + +"@tanstack/react-table@^8.20.0": + version "8.21.3" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.21.3.tgz#2c38c747a5731c1a07174fda764b9c2b1fb5e91b" + integrity sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww== + dependencies: + "@tanstack/table-core" "8.21.3" + +"@tanstack/table-core@8.21.3": + version "8.21.3" + resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.3.tgz#2977727d8fc8dfa079112d9f4d4c019110f1732c" + integrity sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg== + +"@types/babel__core@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" + integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" + integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== + dependencies: + "@babel/types" "^7.28.2" + +"@types/estree@1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@vitejs/plugin-react@^4.3.0": + version "4.7.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz#647af4e7bb75ad3add578e762ad984b90f4a24b9" + integrity sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA== + dependencies: + "@babel/core" "^7.28.0" + "@babel/plugin-transform-react-jsx-self" "^7.27.1" + "@babel/plugin-transform-react-jsx-source" "^7.27.1" + "@rolldown/pluginutils" "1.0.0-beta.27" + "@types/babel__core" "^7.20.5" + react-refresh "^0.17.0" + +baseline-browser-mapping@^2.9.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz#5b09935025bf8a80e29130251e337c6a7fc8cbb9" + integrity sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA== + +browserslist@^4.24.0: + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== + dependencies: + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" + node-releases "^2.0.27" + update-browserslist-db "^1.2.0" + +caniuse-lite@^1.0.30001759: + version "1.0.30001774" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz#0e576b6f374063abcd499d202b9ba1301be29b70" + integrity sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +debug@^4.1.0, debug@^4.3.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +electron-to-chromium@^1.5.263: + version "1.5.302" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz#032a5802b31f7119269959c69fe2015d8dad5edb" + integrity sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg== + +esbuild@^0.25.0: + version "0.25.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5" + integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.25.12" + "@esbuild/android-arm" "0.25.12" + "@esbuild/android-arm64" "0.25.12" + "@esbuild/android-x64" "0.25.12" + "@esbuild/darwin-arm64" "0.25.12" + "@esbuild/darwin-x64" "0.25.12" + "@esbuild/freebsd-arm64" "0.25.12" + "@esbuild/freebsd-x64" "0.25.12" + "@esbuild/linux-arm" "0.25.12" + "@esbuild/linux-arm64" "0.25.12" + "@esbuild/linux-ia32" "0.25.12" + "@esbuild/linux-loong64" "0.25.12" + "@esbuild/linux-mips64el" "0.25.12" + "@esbuild/linux-ppc64" "0.25.12" + "@esbuild/linux-riscv64" "0.25.12" + "@esbuild/linux-s390x" "0.25.12" + "@esbuild/linux-x64" "0.25.12" + "@esbuild/netbsd-arm64" "0.25.12" + "@esbuild/netbsd-x64" "0.25.12" + "@esbuild/openbsd-arm64" "0.25.12" + "@esbuild/openbsd-x64" "0.25.12" + "@esbuild/openharmony-arm64" "0.25.12" + "@esbuild/sunos-x64" "0.25.12" + "@esbuild/win32-arm64" "0.25.12" + "@esbuild/win32-ia32" "0.25.12" + "@esbuild/win32-x64" "0.25.12" + +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +fdir@^6.4.4, fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^4.0.2, picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + +postcss@^8.5.3: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +react-dom@^18.2.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" + integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.2" + +react-refresh@^0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.17.0.tgz#b7e579c3657f23d04eccbe4ad2e58a8ed51e7e53" + integrity sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ== + +react@^18.2.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== + dependencies: + loose-envify "^1.1.0" + +rollup@^4.34.9: + version "4.59.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.59.0.tgz#cf74edac17c1486f562d728a4d923a694abdf06f" + integrity sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.59.0" + "@rollup/rollup-android-arm64" "4.59.0" + "@rollup/rollup-darwin-arm64" "4.59.0" + "@rollup/rollup-darwin-x64" "4.59.0" + "@rollup/rollup-freebsd-arm64" "4.59.0" + "@rollup/rollup-freebsd-x64" "4.59.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.59.0" + "@rollup/rollup-linux-arm-musleabihf" "4.59.0" + "@rollup/rollup-linux-arm64-gnu" "4.59.0" + "@rollup/rollup-linux-arm64-musl" "4.59.0" + "@rollup/rollup-linux-loong64-gnu" "4.59.0" + "@rollup/rollup-linux-loong64-musl" "4.59.0" + "@rollup/rollup-linux-ppc64-gnu" "4.59.0" + "@rollup/rollup-linux-ppc64-musl" "4.59.0" + "@rollup/rollup-linux-riscv64-gnu" "4.59.0" + "@rollup/rollup-linux-riscv64-musl" "4.59.0" + "@rollup/rollup-linux-s390x-gnu" "4.59.0" + "@rollup/rollup-linux-x64-gnu" "4.59.0" + "@rollup/rollup-linux-x64-musl" "4.59.0" + "@rollup/rollup-openbsd-x64" "4.59.0" + "@rollup/rollup-openharmony-arm64" "4.59.0" + "@rollup/rollup-win32-arm64-msvc" "4.59.0" + "@rollup/rollup-win32-ia32-msvc" "4.59.0" + "@rollup/rollup-win32-x64-gnu" "4.59.0" + "@rollup/rollup-win32-x64-msvc" "4.59.0" + fsevents "~2.3.2" + +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== + dependencies: + loose-envify "^1.1.0" + +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +tinyglobby@^0.2.13: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + +update-browserslist-db@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +vite@^6.0.0: + version "6.4.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.4.1.tgz#afbe14518cdd6887e240a4b0221ab6d0ce733f96" + integrity sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g== + dependencies: + esbuild "^0.25.0" + fdir "^6.4.4" + picomatch "^4.0.2" + postcss "^8.5.3" + rollup "^4.34.9" + tinyglobby "^0.2.13" + optionalDependencies: + fsevents "~2.3.3" + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==