From 4ee7246fef1955477e43cd40fd9c1f673ddf85f1 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <42101107+Kevin101Zhang@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:28:26 -0400 Subject: [PATCH 01/10] feat: revamp BOS component Dashboard Nav, BOS component explorer, introduce launchpad prototype, and added 404 fallback. (#867) added BOS component launchpad, and a general 404 fallback. revamped and integrated launchpad with new navigation with style reword for better alignment towards future styles https://www.loom.com/share/bc9ed95387e5451f94fac65eac2bb146?sid=a3763829-bc5c-4c67-af06-9433762570f5 --- frontend/widgets/src/QueryApi.Dashboard.jsx | 1107 +++++++++++------ frontend/widgets/src/QueryApi.IndexerCard.jsx | 1 + .../widgets/src/QueryApi.IndexerExplorer.jsx | 223 +++- frontend/widgets/src/QueryApi.NotFound.jsx | 39 + 4 files changed, 936 insertions(+), 434 deletions(-) create mode 100644 frontend/widgets/src/QueryApi.NotFound.jsx diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index 8515ac46..ac4760bb 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -1,493 +1,808 @@ -const accountId = context.accountId; -const [selected_accountId, selected_indexerName] = props.selectedIndexerPath - ? props.selectedIndexerPath.split("/") - : [undefined, undefined]; - -const activeTab = props.view == "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore" -const activeIndexerView = props.activeIndexerView ?? "editor"; -const limit = 7; -let totalIndexers = 0; - -State.init({ - activeTab: activeTab, - activeIndexerView: activeIndexerView, - my_indexers: [], - all_indexers: [], - selected_indexer: undefined, - selected_account: undefined, -}); +const NoQueryContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + height: 100%; +`; -const Subheading = styled.h2` - display: block; - margin: 0; - font-size: 14px; - line-height: 20px; - color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; - font-weight: ${(p) => (p.bold ? "600" : "400")}; - font-size: ${(p) => (p.small ? "12px" : "14px")}; - overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; - text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; - white-space: nowrap; - outline: none; +const NoQueryText = styled.p` + margin-top: 16px; + font-size: 16px; + font-family: 'Mona Sans', sans-serif; + color: #A1A09A; + text-align: center; `; -const Editor = styled.div` +const NoQuerySVG = styled.svg` + height: 100px; + width: 100%; + color: #A1A09A; `; -const Status = styled.div` + +const CheckboxContainer = styled.div` + margin-bottom: 10px; `; -const Wrapper = styled.div` - margin-inline: 12px; - margin-top: calc(var(--body-top-padding) * -1); +const CheckboxLabel = styled.label` + display: flex; + align-items: center; + cursor: pointer; + font-size: 16px; + margin-bottom: 5px; `; -const NavBarLogo = styled.a` - padding-top: 0.3125rem; - padding-bottom: 0.3125rem; - margin-right: .01rem; - font-size: 1.25rem; - text-decoration: none; - white-space: nowrap; +const SubCheckboxContainer = styled.div` + margin-left: 20px; + border-left: 2px solid #ccc; + padding-left: 10px; `; -const Main = styled.div` - display: block; + +const Checkbox = styled.input` + cursor: pointer; + width: 21.6px; + height: 21.6px; + border-radius: 5.4px; + border: 0.9px solid #DBDBD7; + padding: 5.4px; + background-color: #FDFDFC; + box-shadow: 0 0.9px 1.8px 0 rgba(0, 0, 0, 0.1); + vertical-align: middle; + margin-right: 7.2px; + outline: none; `; -const Section = styled.div` - padding-top: 0px; - border-left: none; - border-right: none; - display: ${(p) => (p.active ? "block" : "none")}; - margin: ${(p) => (p.negativeMargin ? "0 -12px" : "0")}; +// TOP HALF +const Hero = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 349px; + width: 100%; + background: linear-gradient( + 268.88deg, + rgba(2, 255, 133, 0.2) 1.75%, + rgba(2, 133, 255, 0.08) 54.6%, + rgba(2, 27, 255, 0.08) 84.31% + ); + // , url('https://s3-alpha-sig.figma.com/img/f856/12b1/14c8f8fd2894d48314a47b98531b3002?Expires=1720396800&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=iC8KBVqIyZDHU2~xisqW3kuwC8nLk5POGZqHyVGNcAWcLwep3jEocxIrZI9hR5VUfiXwetmD6pXTdHxScqfIMjwvIsccAhEAkzD9t5xasMfuC5vHKel9t96-CGMeMikD3No92ObNZ-eGFdo2QAnrNVNxufsdwhYUKRbXuZSquC2A2qx9kzYxv7pyUjR3QGxg8UkMqhmZiKogoiLL~727aERO3PUIiSlMMH~kRFKVyK4UnJFERuroJ9L3EZTfgBG90EUM5MYTVqLIeeA1gWeYPkfTlYghAWwOx60B2wdLk5WTgmqytRZxbqsCiN8u92ZKZjmBzFcZZcWF9eONAqdDvA__'); + // background-size: 100%; + // background-position: right; + // background-repeat: no-repeat; `; -const Tabs = styled.div` - display: none; - height: 48px; - background: #f8f9fa; - border-top: 1px solid #eceef0; - border-bottom: 1px solid #eceef0; - margin-bottom: ${(p) => (p.noMargin ? "0" : p.halfMargin ? "24px" : "24px")}; +const Headline = styled.h1` + font-family: 'Mona Sans', sans-serif; + font-weight: 700; + width: 369px; + font-size: 24px; + line-height: 31.2px; +`; + +const Subheadline = styled.p` + font-family: 'Mona Sans', sans-serif; + font-weight: 400; + font-size: 16px; + line-height: 18.2px; + color: #717069; + letter-spacing: 1.5%; +`; +const Container = styled.div` display: flex; - margin-left: -12px; - margin-right: -12px; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; +`; - button { - flex: 1; - } +const HeadlineContainer = styled.div` + width: 364px; + height: 193px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-right: 80px; /* Gap between HeadlineContainer and WidgetContainer */ `; -const Content = styled.div` - background-color: #f7f7f7; - padding: 2em; - border-radius: 5px; + +const WidgetContainer = styled.div` + width: 301px; + height: 365px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + box-shadow: 0 8.2px 19.92px 0 rgba(0, 0, 0, 0.1), 0 2.34px 2.34px 0 rgba(0, 0, 0, 0.15); + margin-top: 183px; /* Gap between WidgetContainer and HeadlineContainer */ + background: #fff; + border-radius: 10px; `; -const Title = styled.h1` - font-size: 1.5em; - text-align: center; - color: palevioletred; +const SubContainer = styled.div` + width: 262.5px; + height: 330px; //270px later `; -const TabsButton = styled.button` - font-weight: 600; +const SubContainerTitle = styled.h2` + font-family: 'Product Sans', sans-serif; + font-weight: 700; font-size: 14px; - line-height: 16px; - padding: 0 12px; - position: relative; - color: ${(p) => (p.selected ? "#11181C" : "#687076")}; - background: none; - border: none; - outline: none; - &:hover { - color: #11181c; - } - - &::after { - content: ""; - display: ${(p) => (p.selected ? "block" : "none")}; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 3px; - background: #0091ff; - } + line-height: 14.06px; + color: #7F7E77; + margin-bottom: 6px; `; -const H2 = styled.h2` - font-size: 19px; - line-height: 22px; - color: #11181c; - margin: 0 0 8px; -`; -const Card = styled.div` - border-radius: 12px; - background: #fff; - border: ${(div) => (div.selected ? "1px solid black" : "1px solid #eceef0")}; - box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), - 0px 1px 2px rgba(16, 24, 40, 0.06); + +const MethodsText = styled.div` + display: flex; + align-items: center; + font-size: 12px; + margin-bottom: 8px; `; -const CardBody = styled.div` - padding: 16px; +const MethodsSpan = styled.span` display: flex; - gap: 16px; align-items: center; + justify-content: center; + font-size: 11px; + font-weight: normal; + width: 23px; + height: 17px; + border-radius: 50px; + padding: 4px 6px; + background-color: #F3F3F2; +`; - > * { - min-width: 0; - } +const SubContainerContent = styled.div` + height: 260px; +` +const ScrollableDiv = styled.div` +height: 260px; + +width: 100%; +overflow-x: auto; +overflow-y: auto; + + +&::-webkit-scrollbar { + height: 12px; +} + +&::-webkit-scrollbar-thumb { + background-color: #888; + border-radius: 6px; +} + +&::-webkit-scrollbar-thumb:hover { + background-color: #555; +} + +&::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 6px; +} + +&::-webkit-scrollbar-track-piece { + background: #f9f9f9; +} + +scrollbar-width: thin; +scrollbar-color: #888 #f1f1f1; + +-ms-overflow-style: -ms-autohiding-scrollbar; + +-ms-scroll-chaining: none; +-ms-scroll-snap-type: mandatory; +-ms-scroll-snap-points-x: snapInterval(0%, 100%); `; -const CardFooter = styled.div` +const InputWrapper = styled.div` display: flex; - justify-content: space-around; - flex-wrap: wrap; - gap: 4px; - padding: 16px; - border-top: 1px solid #eceef0; + align-items: center; + width: 364px; + height: 40px; + border: 1px solid #ccc; + border-radius: 6px; + padding: 0; + overflow: hidden; `; -const TextLink = styled.a` - display: block; - margin: 0; - font-size: 14px; - line-height: 20px; - color: ${(p) => (p.bold ? "#11181C !important" : "#687076 !important")}; - font-weight: ${(p) => (p.bold ? "600" : "400")}; - font-size: ${(p) => (p.small ? "12px" : "14px")}; - overflow: ${(p) => (p.ellipsis ? "hidden" : "visible")}; - text-overflow: ${(p) => (p.ellipsis ? "ellipsis" : "unset")}; - white-space: nowrap; +const StyledInput = styled.input` + flex: 1; + height: 100%; + border: none; outline: none; + padding: 8px 12px; + border-radius: 6px 0 0 6px; +`; - &:focus, - &:hover { - text-decoration: underline; - } +const ContractInputMessage = styled.p` + margin-top: 8px; + height: 25px; + font-size: 10px; + color: #D95C4A; + width: 100%; `; -const Thumbnail = styled.a` - display: block; - width: 48px; - height: 48px; - flex-shrink: 0; - border: 1px solid #eceef0; - border-radius: 8px; - overflow: hidden; - outline: none; - transition: border-color 200ms; +const WarningSVG = styled.svg` + height: 16px; + width: 16px; + margin-right: 4px; +` - &:focus, - &:hover { - border-color: #d0d5dd; - } +const SearchButton = styled.button` + width: 84px; + background-color: #37CD83; + border: none; + border-radius: 0px 6px 6px 0px; + color: white; + cursor: pointer; + padding: 8px 16px; + display: flex; + align-items: center; + justify-content: center; +`; - img { - object-fit: cover; - width: 100%; - height: 100%; - } +//BOTTOM HALF +const Divider = styled.div` + height: 40px; + width: 100%; +` +const ExploreIndexersContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; `; -const CardWrapper = styled.div` - margin: 0 0 16px; + +const ExploreIndexersHeading = styled.h2` + font-family: 'Mona Sans', sans-serif; + font-size: 20px; + font-weight: 500; + line-height: 26px; + letter-spacing: 0.015em; + text-align: left; `; -const sharedButtonStyles = ` - display: inline-flex; +const ExploreContent = styled.div` + width: 745px; + display: flex; + flex-direction: column; + gap: 24px; +` + +const SearchIndexerContainer = styled.div` + display: flex; align-items: center; + width: 269px; + height: 40px; + padding: 8px 12px; gap: 8px; - padding: 8px 16px; - margin-bottom: 12px; - height: 32px; - border-radius: 6px; - font-weight: 600; - font-size: 12px; - line-height: 15px; - text-align: center; - cursor: pointer; + border-radius: 50px; + border: 1px solid #E3E3E0; + background-color: white; +`; - &:hover, - &:focus { - text-decoration: none; - outline: none; +const SearchInput = styled.input` + flex: 1; + border: none; + outline: none; + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 14px; + line-height: 21px; + letter-spacing: 2%; + &::placeholder { + color: #a9a9a9; } +`; - i { - color: #7E868C; - } +const SearchIndexerButton = styled.button` + flex:1; + border-radius: 50px; + background-color: #f0f0f0; + border: none; + color: black; + padding: 8px 31px; + cursor: pointer; + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 14px; + line-height: 21px; + letter-spacing: 2%; +`; - .bi-16 { - font-size: 16px; - } +const MagnifyingGlass = styled.svg` + width: 16px; + height: 16px; +`; + +const SearchArrow = styled.svg` + width: 20px; + height: 20px; +` +/** TABLE STYLES*/ +const TableContainer = styled.div` + width: 745px; + margin: 0 auto; +`; + +const Table = styled.table` + width: 100%; + border-collapse: collapse; + border-radius: 8px; + overflow: hidden; `; -const Button = styled.button` - ${sharedButtonStyles} - color: ${(p) => (p.primary ? "#fff" : "#11181C")} !important; - background: ${(p) => (p.primary ? "#0091FF" : "#FBFCFD")}; - border: ${(p) => (p.primary ? "none" : "1px solid #D7DBDF")}; +const TableHeader = styled.thead` + background-color: #F0F0F1; +`; + +const TableHeaderCell = styled.th` + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 10px; + line-height: 14px; + letter-spacing: 2%; + text-align: left; + padding: 8px; +`; - &:hover, - &:focus { - background: ${(p) => (p.primary ? "#0484e5" : "#ECEDEE")}; +const TableRow = styled.tr` + &:nth-child(even) { + background-color: #f9f9f9; } `; -const ButtonLink = styled.a` - ${sharedButtonStyles} - color: ${(p) => { - if (p.primary) return "#fff"; - else if (p.danger) return "#fff"; - else return "#11181C"; - }} !important; - background: ${(p) => { - if (p.primary) return "#0091FF"; - else if (p.danger) return "#dc3545"; - else return "#FBFCFD"; - }}; - border: ${(p) => (p.primary ? "none" : "1px solid #D7DBDF")}; - - &:hover, - &:focus { - background: ${(p) => { - if (p.primary) return "#0484e5"; - else if (p.danger) return "#b22b38"; - else return "#ECEDEE"; - }} -`; - -const SignUpLink = styled.a` - --blue: RGBA(13, 110, 253, 1); - display: ${({ hidden }) => (hidden ? "none" : "inline-block")}; +const TableCell = styled.td` + font-family: 'Mona Sans', sans-serif; + font-weight: 400; font-size: 14px; + line-height: 21px; + letter-spacing: 2%; + padding: 8px; + text-align: left; +`; + +// ORIGINAL STYLED COMPONENTS +const Wrapper = styled.div` + margin-top: calc(var(--body-top-padding) * -1); +`; + +const Main = styled.div` + display: block; +`; + +const Section = styled.div` + padding-top: 0px; + border-left: none; + border-right: none; +`; + +const Tabs = styled.div` + display: flex; + border-bottom: 2px solid #ccc; + background-color: #f0f0f0; +`; + +const TabsButton = styled.button` + flex: 1; + padding: 1rem; + border: none; + background: ${props => (props.selected ? '#3acd83' : 'transparent')}; + color: ${props => (props.selected ? '#fff' : '#333')}; + font-family: 'Mona Sans', sans-serif; + font-size: 1rem; + font-weight: ${props => (props.selected ? 'bold' : 'normal')}; cursor: pointer; - color: var(--blue); - text-decoration: none; - margin-left: 0.1em; - padding: 0; - white-space: nowrap; + transition: background-color 0.3s, color 0.3s; &:hover { - color: var(--blue); - text-decoration: none; + background: #e0e0e0; } +`; + +const AlertText = styled.p` +font-family: 'Mona Sans', sans-serif; +font-size: 14px; +line-height: 21px; +text-align: center; +color:red; +margin: 0; +padding: 0; +bg-color: #f9f9f9; +` + +const LoadingSpinner = () => { + const spinnerStyle = { + width: '40px', + height: '40px', + border: '4px solid rgba(0, 0, 0, 0.1)', + borderLeftColor: 'black', + borderRadius: '50%', + animation: 'spin 1s linear infinite', + textAlign: 'center', + display: 'flex', + justifyContent: 'center', + alignCenter: 'center', + }; + + const LoadingContainer = styled.div` + text-align: center; + width: 100%; + `; + + const LoadingSpinnerContainer = styled.div` + display: flex; + justify-content: center; + font-size: 14px; + ` + return
<>Generating Methods; +}; - &:visited { - color: var(--blue); - text-decoration: none; +const accountId = context.accountId; +const WILD_CARD = '*'; + +const validateContractId = (accountId) => { + accountId = accountId.trim(); + // Check if accountId is a wildcard '*' + if (accountId === WILD_CARD) return true; + // Check if accountId length is between 2 and 64 characters + const isLengthValid = accountId.length >= 2 && accountId.length <= 64; + if (!isLengthValid) return false; + // Check if accountId starts with '*.' || '*' remove for part verification + if (accountId.startsWith('*.')) accountId = accountId.slice(2); + if (accountId.startsWith('*')) accountId = accountId.slice(1); + + const parts = accountId.split('.'); + for (let part of parts) { + if (!part.match(/^[a-z\d]+([-_][a-z\d]+)*$/)) { + return false; + } } -`; -// TODO fix activeTab -// const previousSelectedTab = Storage.privateGet("queryapi:activeTab"); -// if (previousSelectedTab && previousSelectedTab !== state.activeTab) { -// State.update({ -// activeTab: previousSelectedTab, -// }); -// } -const selectTab = (tabName) => { - Storage.privateSet("queryapi:activeTab", tabName); - State.update({ - activeTab: tabName, + return true; +}; +const IS_DEV = `${REPL_EXTERNAL_APP_URL}` === "https://queryapi-frontend-vcqilefdcq-ew.a.run.app" || `${REPL_EXTERNAL_APP_URL}` === "http://localhost:3000"; + +const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); +const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); + +const [allIndexers, setAllIndexers] = useState([]); +const [checkboxState, setCheckboxState] = useState(initialCheckboxState); + +const [checkBoxData, setCheckBoxData] = useState([]); +const [loading, setLoading] = useState(false); +const [contractInputMessage, setContractInputMessage] = useState(''); +const [inputValue, setInputValue] = useState(''); +const [methodCount, setMethodCount] = useState(0); + +const [selectedIndexer, setSelectedIndexer] = useState(props.selectedIndexerPath); + +const handleFetchCheckboxData = async () => { + setCheckBoxData([]); + setMethodCount(0); + setContractInputMessage(''); + + if (!validateContractId(inputValue)) { + setContractInputMessage('Invalid contract id'); + return; + } + + setLoading(true); + + const url = 'https://europe-west1-pagoda-data-stack-prod.cloudfunctions.net/queryapi_wizard'; + asyncFetch(url, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + filter: inputValue, + }), + } + ) + .then(response => { + if (!response.ok) { + setError('There was an error fetching the data'); + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = response.body; + + if (data.length === 0) { + setContractInputMessage('No methods found for this contract'); + setLoading(false); + return; + }; + + setCheckBoxData(data); + setMethodCount(data.length); + setLoading(false); + }).catch(error => { + setLoading(false); + setError('There was an error fetching the data'); + }); + +}; + +const initialCheckboxState = checkBoxData.reduce((acc, item) => { + //Select eveyrthing by default. + acc[item.method_name] = false; + if (item.schema.properties) { + Object.keys(item.schema.properties).forEach(property => { + acc[`${item.method_name}::${property}`] = false; + }); + } + return acc; +}, {}); + +const handleParentChange = (methodName) => { + const newState = { ...checkboxState }; + const isChecked = !checkboxState[methodName]; + newState[methodName] = isChecked; + checkBoxData.forEach(item => { + if (item.method_name === methodName && item.schema.properties) { + Object.keys(item.schema.properties).forEach(property => { + newState[`${methodName}::${property}`] = isChecked; + }); + } }); + setCheckboxState(newState); }; -const selectIndexerPage = (viewName) => { - Storage.privateSet("queryapi:activeIndexerTabView", viewName); - State.update({ - activeIndexerView: viewName, +const handleChildChange = (childId) => { + setCheckboxState({ + ...checkboxState, + [childId]: !checkboxState[childId], }); }; -const indexerView = (accountId, indexerName) => { - const editUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}`; - const statusUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}&view=indexer&activeIndexerView=status`; - const playgroundLink = `https://cloud.hasura.io/public/graphiql?endpoint=${REPL_GRAPHQL_ENDPOINT}/v1/graphql&header=x-hasura-role%3A${accountId.replaceAll( - ".", - "_" - )}`; +useEffect(() => { + Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { + const indexers = []; + Object.keys(data).forEach((accountId) => { + Object.keys(data[accountId]).forEach((functionName) => { + indexers.push({ + accountId: accountId, + indexerName: functionName, + }); + }); + }); + setAllIndexers(indexers) + }); +}); + +const data = allIndexers.map((indexer) => ({ + indexer: indexer.indexerName, + weeklyRequest: indexer.weeklyRequest || 150, + lastUpdated: indexer.lastUpdated || '2023-06-25', + status: indexer.status || 'Active', +})); + +function CustomTable() { return ( - - - - - - -
- - {indexerName} - - - @{accountId} - -
-
- - - selectIndexerPage("status")}> - View Status - - selectIndexerPage("editor")}> - {accountId === context.accountId ? "Edit Indexer" : "View Indexer"} - - - View In Playground - - -
+ + + + + Indexer + Weekly Request + Last Updated + Status + + + + {data.map((row, index) => ( + + {row.indexer} + {row.weeklyRequest} + {row.lastUpdated} + {row.status} + + ))} + +
+
); +} + +const selectTab = (tabName) => { + Storage.privateSet("queryapi:activeTab", tabName); + setActiveTab(tabName); +}; + +const selectIndexerPage = (viewName) => { + Storage.privateSet("queryapi:activeIndexerTabView", viewName); + setActiveIndexerTabView(viewName); }; return ( - + + {IS_DEV && ( + selectTab("launchpad")} + selected={activeTab === "launchpad"} + > + Launchpad + + )} + selectTab("explore")} - selected={state.activeTab === "explore"} + selected={activeTab === "explore"} > Explore Indexers - {state.activeTab == "create-new-indexer" && ( - selectTab("create-new-indexer")} - selected={state.activeTab === "create-new-indexer"} - > - Create New Indexer - - )} - {props.selectedIndexerPath && ( - <> - selectTab("indexer")} - selected={state.activeTab === "indexer"} - > - Indexer ({props.selectedIndexerPath}) - - - )} + selectTab("indexer")} + selected={activeTab === "indexer"} + > + Indexer ({selectedIndexer}) + + +
-
- selectTab("explore")} - > + {activeTab === 'launchpad' && IS_DEV && ( +
+ Please note that this page is currently under development. Features may be incomplete or inaccurate + + + + Launch an indexer in minutes + Get a working indexer exportable to your Near react application faster than ever. Extract on-chain data, and easily query it using GraphQL endpoints and subscriptions. + + setInputValue(e.target.value)} + onKeyPress={(event) => event.key === 'Enter' && handleFetchCheckboxData()} + /> + Start + + {contractInputMessage ?? <> {contractInputMessage}} + + + + + Customize indexer + + {loading ? ( + + + + ) : (checkBoxData.length === 0) ? + <> + + + + No smart contract address entered + + + : ( +
+ {checkBoxData.length > 0 && ( + + Methods {methodCount} + + )} + < ScrollableDiv > + { + checkBoxData.length > 0 && ( + <> + {checkBoxData.map((item, index) => ( + + + handleParentChange(item.method_name)} + /> + {item.method_name} + + {item.schema.properties && ( + + {Object.keys(item.schema.properties).map((property, subIndex) => ( + + handleChildChange(`${item.method_name}::${property}`)} + /> + {property}: {item.schema.properties[property].type} + + ))} + + )} + + ))} + + ) + } + +
+ )} +
+
+
+
+
+ + + + Explore indexers on Near + + + + + + + + {CustomTable()} + + +
+ )} + + {activeTab === 'explore' && ( +
+
+ )} + + {activeTab === "create-new-indexer" && ( +
+ - QueryApi - - - - (Documentation) - -
- { - State.update({ - activeTab: "create-new-indexer", - selected_indexer: "", - }); - selectTab("create-new-indexer"); - }} - > - Create New Indexer - - {state.my_indexers.length > 0 && ( -

- {accountId}'s Indexers - ({state.my_indexers.length}) -

- )} - -
-
-
- {state.activeTab === "create-new-indexer" && ( -
- -
- )} -
-
- - {state.indexers.length > 0 && - (state.selected_indexer != undefined ? ( -

{state.selected_indexer}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} +
+ )} + + {activeTab === 'indexer' && ( +
- - {state.activeTab === "create-new-indexer" && ( -
- {state.indexers.length > 0 && - (state.selected_indexer != undefined ? ( -

{state.selected_indexer}

- ) : ( -

{`${state.indexers[0].accountId}/${state.indexers[0].indexerName}`}

- ))} - -
- )} -
+
+ )} + + {!['launchpad', 'explore', 'indexer', 'create-new-indexer'].includes(activeTab) && ( + + )} +
-
+
); + + + + diff --git a/frontend/widgets/src/QueryApi.IndexerCard.jsx b/frontend/widgets/src/QueryApi.IndexerCard.jsx index d82e978d..72f63cf9 100644 --- a/frontend/widgets/src/QueryApi.IndexerCard.jsx +++ b/frontend/widgets/src/QueryApi.IndexerCard.jsx @@ -9,6 +9,7 @@ const indexer = { }; const editUrl = `https://dev.near.org/${REPL_ACCOUNT_ID}/widget/QueryApi.App?selectedIndexerPath=${accountId}/${indexerName}`; + const playgroundLink = `https://cloud.hasura.io/public/graphiql?endpoint=${REPL_GRAPHQL_ENDPOINT}/v1/graphql&header=x-hasura-role%3A${accountId.replace(/\./g, '_')}`; const formatNumberWithCommas = (number) => number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); diff --git a/frontend/widgets/src/QueryApi.IndexerExplorer.jsx b/frontend/widgets/src/QueryApi.IndexerExplorer.jsx index 55060417..abb9ca61 100644 --- a/frontend/widgets/src/QueryApi.IndexerExplorer.jsx +++ b/frontend/widgets/src/QueryApi.IndexerExplorer.jsx @@ -130,22 +130,35 @@ const Text = styled.p` } } `; - const Items = styled.div` display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; + padding: 1rem; @media (max-width: 1200px) { grid-template-columns: repeat(2, minmax(0, 1fr)); } @media (max-width: 800px) { - grid-template-columns: minmax(0, 1fr); + grid-template-columns: 1fr; + } +`; + +const Item = styled.div` + background-color: #fff; + border: 1px solid #ddd; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + overflow: hidden; + transition: transform 0.3s, box-shadow 0.3s; + + &:hover { + transform: translateY(-4px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2); } `; -const Item = styled.div``; const Button = styled.button` display: block; @@ -243,6 +256,106 @@ const TextLink = styled.a` } `; + +const NavBarContainer = styled.div` + display: flex; + align-items: center; + background-color: #f0f0f0; + padding: 0.5rem 1rem; + border-bottom: 1px solid #ccc; + justify-content: space-between; +`; + +const LeftGroup = styled.div` + display: flex; + align-items: center; +`; + +const NavBarLogo = styled.a` + display: flex; + align-items: center; + color: #333; + text-decoration: none; + font-size: 0.875rem; + font-weight: bold; + margin-right: 1rem; + &:hover { + text-decoration: none; + color: #333; + } + +`; + +const SignUpLink = styled.a` + color: #0070f3; + text-decoration: none; + font-size: 0.75rem; + margin-right: 1rem; +`; + +const ButtonWrapper = styled.div` + display: flex; + align-items: center; +`; +const ButtonLink = styled.a` + display: inline-block; + padding: 0.5rem 1rem; + background-color: black; + color: #fff; + text-decoration: none; + border-radius: 4px; + font-size: 0.75rem; + font-weight: bold; + cursor: pointer; + transition: background-color 0.3s, color 0.3s, transform 0.2s; + + &:hover { + background-color: #333; + color: #fff; + text-decoration: none; + outline: none; + } + + &:focus { + outline: none; + text-decoration: none; + } +`; + +const ToggleWrapper = styled.div` + display: flex; + align-items: center; +`; + +const ToggleButton = styled.button` + flex: 1; + padding: 0.5rem; + border: none; + border-radius: 20px; + background-color: ${props => (props.selected ? 'black' : '#e0e0e0')}; + color: ${props => (props.selected ? '#fff' : '#333')}; + font-size: 0.75rem; + font-weight: ${props => (props.selected ? 'bold' : 'normal')}; + cursor: pointer; + transition: background-color 0.3s, color 0.3s, transform 0.2s; + text-align: center; + min-width: 100px; + max-width: 150px; + + &:not(:last-child) { + margin-right: 4px; + } + + &:hover { + background-color: ${props => (props.selected ? '#333' : '#d0d0d0')}; + color: ${props => (props.selected ? '#fff' : '#000')}; + } + + &:focus { + outline: none; + } +`; + const LoadingSpinner = () => { const spinnerStyle = { width: '40px', @@ -277,21 +390,59 @@ const LoadingSpinner = () => { }; return ( - - - setSelectedTab("my-indexers")} - selected={selectedTab === "my-indexers"} - > - My Indexers - - setSelectedTab("all")} - selected={selectedTab === "all"} - > - All - - + + + + + + + QueryApi + + + + (Documentation) + + + + { + setActiveTab("create-new-indexer"); + setSelectedIndexerName(""); + selectTab("create-new-indexer"); + }} + > + Create New Indexer + + + + + + setSelectedTab("my-indexers")} + selected={selectedTab === "my-indexers"} + > + My Indexers + + setSelectedTab("all")} + selected={selectedTab === "all"} + > + All Indexers + + + + {error && {error}} {selectedTab === "all" && ( @@ -302,16 +453,14 @@ return ( ) : ( - <> - {allIndexers.map((indexer, i) => ( - - - - ))} - + {allIndexers.map((indexer, i) => ( + + + + ))} )} @@ -338,16 +487,14 @@ return ( ) : ( - <> - {myIndexers.map((indexer, i) => ( - - - - ))} - + {myIndexers.map((indexer, i) => ( + + + + ))} )} diff --git a/frontend/widgets/src/QueryApi.NotFound.jsx b/frontend/widgets/src/QueryApi.NotFound.jsx new file mode 100644 index 00000000..173659f1 --- /dev/null +++ b/frontend/widgets/src/QueryApi.NotFound.jsx @@ -0,0 +1,39 @@ +const PageNotFoundContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100vh; + text-align: center; + background-color: #f9f9f9; + color: #333; +`; + +const Heading = styled.h1` + font-size: 3rem; + margin: 0; +`; + +const Subheading = styled.p` + font-size: 1.5rem; + margin: 1rem 0; +`; + +const HomeLink = styled.a` + font-size: 1rem; + color: #007bff; + text-decoration: none; + margin-top: 2rem; + + &:hover { + text-decoration: underline; + } +`; + +return ( + + 404 + Page Not Found + Go back to Home + +); From 3f1056feac87a3ae78d370c36ff6bc6fb4725681 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <42101107+Kevin101Zhang@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:48:03 -0400 Subject: [PATCH 02/10] chore: code separation of launchpad (#905) code cleanup/separation into separate BOS component --- frontend/widgets/src/QueryApi.Dashboard.jsx | 675 +------------------- frontend/widgets/src/QueryApi.Launchpad.jsx | 671 +++++++++++++++++++ 2 files changed, 674 insertions(+), 672 deletions(-) create mode 100644 frontend/widgets/src/QueryApi.Launchpad.jsx diff --git a/frontend/widgets/src/QueryApi.Dashboard.jsx b/frontend/widgets/src/QueryApi.Dashboard.jsx index ac4760bb..5cf01250 100644 --- a/frontend/widgets/src/QueryApi.Dashboard.jsx +++ b/frontend/widgets/src/QueryApi.Dashboard.jsx @@ -1,373 +1,3 @@ -const NoQueryContainer = styled.div` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; - height: 100%; -`; - -const NoQueryText = styled.p` - margin-top: 16px; - font-size: 16px; - font-family: 'Mona Sans', sans-serif; - color: #A1A09A; - text-align: center; -`; - -const NoQuerySVG = styled.svg` - height: 100px; - width: 100%; - color: #A1A09A; -`; - -const CheckboxContainer = styled.div` - margin-bottom: 10px; -`; - -const CheckboxLabel = styled.label` - display: flex; - align-items: center; - cursor: pointer; - font-size: 16px; - margin-bottom: 5px; -`; - -const SubCheckboxContainer = styled.div` - margin-left: 20px; - border-left: 2px solid #ccc; - padding-left: 10px; -`; - -const Checkbox = styled.input` - cursor: pointer; - width: 21.6px; - height: 21.6px; - border-radius: 5.4px; - border: 0.9px solid #DBDBD7; - padding: 5.4px; - background-color: #FDFDFC; - box-shadow: 0 0.9px 1.8px 0 rgba(0, 0, 0, 0.1); - vertical-align: middle; - margin-right: 7.2px; - outline: none; -`; - -// TOP HALF -const Hero = styled.div` - display: flex; - justify-content: center; - align-items: center; - height: 349px; - width: 100%; - background: linear-gradient( - 268.88deg, - rgba(2, 255, 133, 0.2) 1.75%, - rgba(2, 133, 255, 0.08) 54.6%, - rgba(2, 27, 255, 0.08) 84.31% - ); - // , url('https://s3-alpha-sig.figma.com/img/f856/12b1/14c8f8fd2894d48314a47b98531b3002?Expires=1720396800&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=iC8KBVqIyZDHU2~xisqW3kuwC8nLk5POGZqHyVGNcAWcLwep3jEocxIrZI9hR5VUfiXwetmD6pXTdHxScqfIMjwvIsccAhEAkzD9t5xasMfuC5vHKel9t96-CGMeMikD3No92ObNZ-eGFdo2QAnrNVNxufsdwhYUKRbXuZSquC2A2qx9kzYxv7pyUjR3QGxg8UkMqhmZiKogoiLL~727aERO3PUIiSlMMH~kRFKVyK4UnJFERuroJ9L3EZTfgBG90EUM5MYTVqLIeeA1gWeYPkfTlYghAWwOx60B2wdLk5WTgmqytRZxbqsCiN8u92ZKZjmBzFcZZcWF9eONAqdDvA__'); - // background-size: 100%; - // background-position: right; - // background-repeat: no-repeat; -`; - -const Headline = styled.h1` - font-family: 'Mona Sans', sans-serif; - font-weight: 700; - width: 369px; - font-size: 24px; - line-height: 31.2px; -`; - -const Subheadline = styled.p` - font-family: 'Mona Sans', sans-serif; - font-weight: 400; - font-size: 16px; - line-height: 18.2px; - color: #717069; - letter-spacing: 1.5%; -`; - -const Container = styled.div` - display: flex; - justify-content: center; - align-items: center; - height: 100%; - width: 100%; -`; - -const HeadlineContainer = styled.div` - width: 364px; - height: 193px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin-right: 80px; /* Gap between HeadlineContainer and WidgetContainer */ -`; - -const WidgetContainer = styled.div` - width: 301px; - height: 365px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - box-shadow: 0 8.2px 19.92px 0 rgba(0, 0, 0, 0.1), 0 2.34px 2.34px 0 rgba(0, 0, 0, 0.15); - margin-top: 183px; /* Gap between WidgetContainer and HeadlineContainer */ - background: #fff; - border-radius: 10px; -`; - -const SubContainer = styled.div` - width: 262.5px; - height: 330px; //270px later -`; - -const SubContainerTitle = styled.h2` - font-family: 'Product Sans', sans-serif; - font-weight: 700; - font-size: 14px; - line-height: 14.06px; - color: #7F7E77; - margin-bottom: 6px; -`; - -const MethodsText = styled.div` - display: flex; - align-items: center; - font-size: 12px; - margin-bottom: 8px; -`; - -const MethodsSpan = styled.span` - display: flex; - align-items: center; - justify-content: center; - font-size: 11px; - font-weight: normal; - width: 23px; - height: 17px; - border-radius: 50px; - padding: 4px 6px; - background-color: #F3F3F2; -`; - -const SubContainerContent = styled.div` - height: 260px; -` -const ScrollableDiv = styled.div` -height: 260px; - -width: 100%; -overflow-x: auto; -overflow-y: auto; - - -&::-webkit-scrollbar { - height: 12px; -} - -&::-webkit-scrollbar-thumb { - background-color: #888; - border-radius: 6px; -} - -&::-webkit-scrollbar-thumb:hover { - background-color: #555; -} - -&::-webkit-scrollbar-track { - background: #f1f1f1; - border-radius: 6px; -} - -&::-webkit-scrollbar-track-piece { - background: #f9f9f9; -} - -scrollbar-width: thin; -scrollbar-color: #888 #f1f1f1; - --ms-overflow-style: -ms-autohiding-scrollbar; - --ms-scroll-chaining: none; --ms-scroll-snap-type: mandatory; --ms-scroll-snap-points-x: snapInterval(0%, 100%); -`; - -const InputWrapper = styled.div` - display: flex; - align-items: center; - width: 364px; - height: 40px; - border: 1px solid #ccc; - border-radius: 6px; - padding: 0; - overflow: hidden; -`; - -const StyledInput = styled.input` - flex: 1; - height: 100%; - border: none; - outline: none; - padding: 8px 12px; - border-radius: 6px 0 0 6px; -`; - -const ContractInputMessage = styled.p` - margin-top: 8px; - height: 25px; - font-size: 10px; - color: #D95C4A; - width: 100%; -`; - -const WarningSVG = styled.svg` - height: 16px; - width: 16px; - margin-right: 4px; -` - -const SearchButton = styled.button` - width: 84px; - background-color: #37CD83; - border: none; - border-radius: 0px 6px 6px 0px; - color: white; - cursor: pointer; - padding: 8px 16px; - display: flex; - align-items: center; - justify-content: center; -`; - -//BOTTOM HALF -const Divider = styled.div` - height: 40px; - width: 100%; -` -const ExploreIndexersContainer = styled.div` - display: flex; - justify-content: center; - align-items: center; - height: 100%; - width: 100%; -`; - - -const ExploreIndexersHeading = styled.h2` - font-family: 'Mona Sans', sans-serif; - font-size: 20px; - font-weight: 500; - line-height: 26px; - letter-spacing: 0.015em; - text-align: left; -`; - -const ExploreContent = styled.div` - width: 745px; - display: flex; - flex-direction: column; - gap: 24px; -` - -const SearchIndexerContainer = styled.div` - display: flex; - align-items: center; - width: 269px; - height: 40px; - padding: 8px 12px; - gap: 8px; - border-radius: 50px; - border: 1px solid #E3E3E0; - background-color: white; -`; - -const SearchInput = styled.input` - flex: 1; - border: none; - outline: none; - font-family: 'Mona Sans', sans-serif; - font-weight: 450; - font-size: 14px; - line-height: 21px; - letter-spacing: 2%; - &::placeholder { - color: #a9a9a9; - } -`; - -const SearchIndexerButton = styled.button` - flex:1; - border-radius: 50px; - background-color: #f0f0f0; - border: none; - color: black; - padding: 8px 31px; - cursor: pointer; - font-family: 'Mona Sans', sans-serif; - font-weight: 450; - font-size: 14px; - line-height: 21px; - letter-spacing: 2%; -`; - -const MagnifyingGlass = styled.svg` - width: 16px; - height: 16px; -`; - -const SearchArrow = styled.svg` - width: 20px; - height: 20px; -` -/** TABLE STYLES*/ -const TableContainer = styled.div` - width: 745px; - margin: 0 auto; -`; - -const Table = styled.table` - width: 100%; - border-collapse: collapse; - border-radius: 8px; - overflow: hidden; -`; - -const TableHeader = styled.thead` - background-color: #F0F0F1; -`; - -const TableHeaderCell = styled.th` - font-family: 'Mona Sans', sans-serif; - font-weight: 450; - font-size: 10px; - line-height: 14px; - letter-spacing: 2%; - text-align: left; - padding: 8px; -`; - -const TableRow = styled.tr` - &:nth-child(even) { - background-color: #f9f9f9; - } -`; - -const TableCell = styled.td` - font-family: 'Mona Sans', sans-serif; - font-weight: 400; - font-size: 14px; - line-height: 21px; - letter-spacing: 2%; - padding: 8px; - text-align: left; -`; - -// ORIGINAL STYLED COMPONENTS const Wrapper = styled.div` margin-top: calc(var(--body-top-padding) * -1); `; @@ -405,211 +35,12 @@ const TabsButton = styled.button` } `; -const AlertText = styled.p` -font-family: 'Mona Sans', sans-serif; -font-size: 14px; -line-height: 21px; -text-align: center; -color:red; -margin: 0; -padding: 0; -bg-color: #f9f9f9; -` - -const LoadingSpinner = () => { - const spinnerStyle = { - width: '40px', - height: '40px', - border: '4px solid rgba(0, 0, 0, 0.1)', - borderLeftColor: 'black', - borderRadius: '50%', - animation: 'spin 1s linear infinite', - textAlign: 'center', - display: 'flex', - justifyContent: 'center', - alignCenter: 'center', - }; - - const LoadingContainer = styled.div` - text-align: center; - width: 100%; - `; - - const LoadingSpinnerContainer = styled.div` - display: flex; - justify-content: center; - font-size: 14px; - ` - return
<>Generating Methods; -}; - -const accountId = context.accountId; -const WILD_CARD = '*'; - -const validateContractId = (accountId) => { - accountId = accountId.trim(); - // Check if accountId is a wildcard '*' - if (accountId === WILD_CARD) return true; - // Check if accountId length is between 2 and 64 characters - const isLengthValid = accountId.length >= 2 && accountId.length <= 64; - if (!isLengthValid) return false; - // Check if accountId starts with '*.' || '*' remove for part verification - if (accountId.startsWith('*.')) accountId = accountId.slice(2); - if (accountId.startsWith('*')) accountId = accountId.slice(1); - - const parts = accountId.split('.'); - for (let part of parts) { - if (!part.match(/^[a-z\d]+([-_][a-z\d]+)*$/)) { - return false; - } - } - - return true; -}; const IS_DEV = `${REPL_EXTERNAL_APP_URL}` === "https://queryapi-frontend-vcqilefdcq-ew.a.run.app" || `${REPL_EXTERNAL_APP_URL}` === "http://localhost:3000"; - +const accountId = context.accountId; const [activeTab, setActiveTab] = useState(props.view === "create-new-indexer" ? "create-new-indexer" : props.selectedIndexerPath ? "indexer" : "explore"); const [activeIndexerTabView, setActiveIndexerTabView] = useState(props.activeIndexerView ?? "editor"); - -const [allIndexers, setAllIndexers] = useState([]); -const [checkboxState, setCheckboxState] = useState(initialCheckboxState); - -const [checkBoxData, setCheckBoxData] = useState([]); -const [loading, setLoading] = useState(false); -const [contractInputMessage, setContractInputMessage] = useState(''); -const [inputValue, setInputValue] = useState(''); -const [methodCount, setMethodCount] = useState(0); - const [selectedIndexer, setSelectedIndexer] = useState(props.selectedIndexerPath); -const handleFetchCheckboxData = async () => { - setCheckBoxData([]); - setMethodCount(0); - setContractInputMessage(''); - - if (!validateContractId(inputValue)) { - setContractInputMessage('Invalid contract id'); - return; - } - - setLoading(true); - - const url = 'https://europe-west1-pagoda-data-stack-prod.cloudfunctions.net/queryapi_wizard'; - asyncFetch(url, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - filter: inputValue, - }), - } - ) - .then(response => { - if (!response.ok) { - setError('There was an error fetching the data'); - throw new Error(`HTTP error! status: ${response.status}`); - } - const data = response.body; - - if (data.length === 0) { - setContractInputMessage('No methods found for this contract'); - setLoading(false); - return; - }; - - setCheckBoxData(data); - setMethodCount(data.length); - setLoading(false); - }).catch(error => { - setLoading(false); - setError('There was an error fetching the data'); - }); - -}; - -const initialCheckboxState = checkBoxData.reduce((acc, item) => { - //Select eveyrthing by default. - acc[item.method_name] = false; - if (item.schema.properties) { - Object.keys(item.schema.properties).forEach(property => { - acc[`${item.method_name}::${property}`] = false; - }); - } - return acc; -}, {}); - -const handleParentChange = (methodName) => { - const newState = { ...checkboxState }; - const isChecked = !checkboxState[methodName]; - newState[methodName] = isChecked; - checkBoxData.forEach(item => { - if (item.method_name === methodName && item.schema.properties) { - Object.keys(item.schema.properties).forEach(property => { - newState[`${methodName}::${property}`] = isChecked; - }); - } - }); - setCheckboxState(newState); -}; - -const handleChildChange = (childId) => { - setCheckboxState({ - ...checkboxState, - [childId]: !checkboxState[childId], - }); -}; - -useEffect(() => { - Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { - const indexers = []; - Object.keys(data).forEach((accountId) => { - Object.keys(data[accountId]).forEach((functionName) => { - indexers.push({ - accountId: accountId, - indexerName: functionName, - }); - }); - }); - setAllIndexers(indexers) - }); -}); - -const data = allIndexers.map((indexer) => ({ - indexer: indexer.indexerName, - weeklyRequest: indexer.weeklyRequest || 150, - lastUpdated: indexer.lastUpdated || '2023-06-25', - status: indexer.status || 'Active', -})); - -function CustomTable() { - return ( - - - - - Indexer - Weekly Request - Last Updated - Status - - - - {data.map((row, index) => ( - - {row.indexer} - {row.weeklyRequest} - {row.lastUpdated} - {row.status} - - ))} - -
-
- ); -} - const selectTab = (tabName) => { Storage.privateSet("queryapi:activeTab", tabName); setActiveTab(tabName); @@ -654,113 +85,13 @@ return (
{activeTab === 'launchpad' && IS_DEV && (
- Please note that this page is currently under development. Features may be incomplete or inaccurate - - - - Launch an indexer in minutes - Get a working indexer exportable to your Near react application faster than ever. Extract on-chain data, and easily query it using GraphQL endpoints and subscriptions. - - setInputValue(e.target.value)} - onKeyPress={(event) => event.key === 'Enter' && handleFetchCheckboxData()} - /> - Start - - {contractInputMessage ?? <> {contractInputMessage}} - - - - - Customize indexer - - {loading ? ( - - - - ) : (checkBoxData.length === 0) ? - <> - - - - No smart contract address entered - - - : ( -
- {checkBoxData.length > 0 && ( - - Methods {methodCount} - - )} - < ScrollableDiv > - { - checkBoxData.length > 0 && ( - <> - {checkBoxData.map((item, index) => ( - - - handleParentChange(item.method_name)} - /> - {item.method_name} - - {item.schema.properties && ( - - {Object.keys(item.schema.properties).map((property, subIndex) => ( - - handleChildChange(`${item.method_name}::${property}`)} - /> - {property}: {item.schema.properties[property].type} - - ))} - - )} - - ))} - - ) - } - -
- )} -
-
-
-
-
- - - - Explore indexers on Near - - - - - - - - {CustomTable()} - - +
)} {activeTab === 'explore' && (
- +
)} diff --git a/frontend/widgets/src/QueryApi.Launchpad.jsx b/frontend/widgets/src/QueryApi.Launchpad.jsx new file mode 100644 index 00000000..fd0a0176 --- /dev/null +++ b/frontend/widgets/src/QueryApi.Launchpad.jsx @@ -0,0 +1,671 @@ +const AlertText = styled.p` +font-family: 'Mona Sans', sans-serif; +font-size: 14px; +line-height: 21px; +text-align: center; +color:red; +margin: 0; +padding: 0; +bg-color: #f9f9f9; +`; + +const NoQueryContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + height: 100%; +`; + +const NoQueryText = styled.p` + margin-top: 16px; + font-size: 16px; + font-family: 'Mona Sans', sans-serif; + color: #A1A09A; + text-align: center; +`; + +const NoQuerySVG = styled.svg` + height: 100px; + width: 100%; + color: #A1A09A; +`; + +const CheckboxContainer = styled.div` + margin-bottom: 10px; +`; + +const CheckboxLabel = styled.label` + display: flex; + align-items: center; + cursor: pointer; + font-size: 16px; + margin-bottom: 5px; +`; + +const SubCheckboxContainer = styled.div` + margin-left: 20px; + border-left: 2px solid #ccc; + padding-left: 10px; +`; + +const Checkbox = styled.input` + cursor: pointer; + width: 21.6px; + height: 21.6px; + border-radius: 5.4px; + border: 0.9px solid #DBDBD7; + padding: 5.4px; + background-color: #FDFDFC; + box-shadow: 0 0.9px 1.8px 0 rgba(0, 0, 0, 0.1); + vertical-align: middle; + margin-right: 7.2px; + outline: none; +`; + +// TOP HALF +const Hero = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 349px; + width: 100%; + background: linear-gradient( + 268.88deg, + rgba(2, 255, 133, 0.2) 1.75%, + rgba(2, 133, 255, 0.08) 54.6%, + rgba(2, 27, 255, 0.08) 84.31% + ); + // , url('https://s3-alpha-sig.figma.com/img/f856/12b1/14c8f8fd2894d48314a47b98531b3002?Expires=1720396800&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=iC8KBVqIyZDHU2~xisqW3kuwC8nLk5POGZqHyVGNcAWcLwep3jEocxIrZI9hR5VUfiXwetmD6pXTdHxScqfIMjwvIsccAhEAkzD9t5xasMfuC5vHKel9t96-CGMeMikD3No92ObNZ-eGFdo2QAnrNVNxufsdwhYUKRbXuZSquC2A2qx9kzYxv7pyUjR3QGxg8UkMqhmZiKogoiLL~727aERO3PUIiSlMMH~kRFKVyK4UnJFERuroJ9L3EZTfgBG90EUM5MYTVqLIeeA1gWeYPkfTlYghAWwOx60B2wdLk5WTgmqytRZxbqsCiN8u92ZKZjmBzFcZZcWF9eONAqdDvA__'); + // background-size: 100%; + // background-position: right; + // background-repeat: no-repeat; +`; + +const Headline = styled.h1` + font-family: 'Mona Sans', sans-serif; + font-weight: 700; + width: 369px; + font-size: 24px; + line-height: 31.2px; +`; + +const Subheadline = styled.p` + font-family: 'Mona Sans', sans-serif; + font-weight: 400; + font-size: 16px; + line-height: 18.2px; + color: #717069; + letter-spacing: 1.5%; +`; + +const Container = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; +`; + +const HeadlineContainer = styled.div` + width: 364px; + height: 193px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin-right: 80px; /* Gap between HeadlineContainer and WidgetContainer */ +`; + +const WidgetContainer = styled.div` + width: 301px; + height: 365px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + box-shadow: 0 8.2px 19.92px 0 rgba(0, 0, 0, 0.1), 0 2.34px 2.34px 0 rgba(0, 0, 0, 0.15); + margin-top: 183px; /* Gap between WidgetContainer and HeadlineContainer */ + background: #fff; + border-radius: 10px; +`; + +const SubContainer = styled.div` + width: 262.5px; + height: 330px; //270px later +`; + +const SubContainerTitle = styled.h2` + font-family: 'Product Sans', sans-serif; + font-weight: 700; + font-size: 14px; + line-height: 14.06px; + color: #7F7E77; + margin-bottom: 6px; +`; + +const MethodsText = styled.div` + display: flex; + align-items: center; + font-size: 12px; + margin-bottom: 8px; +`; + +const MethodsSpan = styled.span` + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: normal; + width: 23px; + height: 17px; + border-radius: 50px; + padding: 4px 6px; + background-color: #F3F3F2; +`; + +const SubContainerContent = styled.div` + height: 260px; +` +const ScrollableDiv = styled.div` +height: 260px; + +width: 100%; +overflow-x: auto; +overflow-y: auto; + + +&::-webkit-scrollbar { + height: 12px; +} + +&::-webkit-scrollbar-thumb { + background-color: #888; + border-radius: 6px; +} + +&::-webkit-scrollbar-thumb:hover { + background-color: #555; +} + +&::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 6px; +} + +&::-webkit-scrollbar-track-piece { + background: #f9f9f9; +} + +scrollbar-width: thin; +scrollbar-color: #888 #f1f1f1; + +-ms-overflow-style: -ms-autohiding-scrollbar; + +-ms-scroll-chaining: none; +-ms-scroll-snap-type: mandatory; +-ms-scroll-snap-points-x: snapInterval(0%, 100%); +`; + +const InputWrapper = styled.div` + display: flex; + align-items: center; + width: 364px; + height: 40px; + border: 1px solid #ccc; + border-radius: 6px; + padding: 0; + overflow: hidden; +`; + +const StyledInput = styled.input` + flex: 1; + height: 100%; + border: none; + outline: none; + padding: 8px 12px; + border-radius: 6px 0 0 6px; +`; + +const ContractInputMessage = styled.p` + margin-top: 8px; + height: 25px; + font-size: 10px; + color: #D95C4A; + width: 100%; +`; + +const WarningSVG = styled.svg` + height: 16px; + width: 16px; + margin-right: 4px; +` + +const SearchButton = styled.button` + width: 84px; + background-color: #37CD83; + border: none; + border-radius: 0px 6px 6px 0px; + color: white; + cursor: pointer; + padding: 8px 16px; + display: flex; + align-items: center; + justify-content: center; +`; + +//BOTTOM HALF +const Divider = styled.div` + height: 40px; + width: 100%; +` +const ExploreIndexersContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; +`; + + +const ExploreIndexersHeading = styled.h2` + font-family: 'Mona Sans', sans-serif; + font-size: 20px; + font-weight: 500; + line-height: 26px; + letter-spacing: 0.015em; + text-align: left; +`; + +const ExploreContent = styled.div` + width: 745px; + display: flex; + flex-direction: column; + gap: 24px; +` + +const SearchIndexerContainer = styled.div` + display: flex; + align-items: center; + width: 269px; + height: 40px; + padding: 8px 12px; + gap: 8px; + border-radius: 50px; + border: 1px solid #E3E3E0; + background-color: white; +`; + +const SearchInput = styled.input` + flex: 1; + border: none; + outline: none; + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 14px; + line-height: 21px; + letter-spacing: 2%; + &::placeholder { + color: #a9a9a9; + } +`; + +const SearchIndexerButton = styled.button` + flex:1; + border-radius: 50px; + background-color: #f0f0f0; + border: none; + color: black; + padding: 8px 31px; + cursor: pointer; + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 14px; + line-height: 21px; + letter-spacing: 2%; +`; + +const MagnifyingGlass = styled.svg` + width: 16px; + height: 16px; +`; + +const SearchArrow = styled.svg` + width: 20px; + height: 20px; +` +/** TABLE STYLES*/ +const TableContainer = styled.div` + width: 745px; + margin: 0 auto; +`; + +const Table = styled.table` + width: 100%; + border-collapse: collapse; + border-radius: 8px; + overflow: hidden; +`; + +const TableHeader = styled.thead` + background-color: #F0F0F1; +`; + +const TableHeaderCell = styled.th` + font-family: 'Mona Sans', sans-serif; + font-weight: 450; + font-size: 10px; + line-height: 14px; + letter-spacing: 2%; + text-align: left; + padding: 8px; +`; + +const TableRow = styled.tr` + &:nth-child(even) { + background-color: #f9f9f9; + } +`; + +const TableCell = styled.td` + font-family: 'Mona Sans', sans-serif; + font-weight: 400; + font-size: 14px; + line-height: 21px; + letter-spacing: 2%; + padding: 8px; + text-align: left; +`; + +const LoadingSpinner = () => { + const spinnerStyle = { + width: '40px', + height: '40px', + border: '4px solid rgba(0, 0, 0, 0.1)', + borderLeftColor: 'black', + borderRadius: '50%', + animation: 'spin 1s linear infinite', + textAlign: 'center', + display: 'flex', + justifyContent: 'center', + alignCenter: 'center', + }; + + const LoadingContainer = styled.div` + text-align: center; + width: 100%; + `; + + const LoadingSpinnerContainer = styled.div` + display: flex; + justify-content: center; + font-size: 14px; + ` + return
<>Generating Methods; +}; + + +const WILD_CARD = '*'; +const validateContractId = (accountId) => { + accountId = accountId.trim(); + // Check if accountId is a wildcard '*' + if (accountId === WILD_CARD) return true; + // Check if accountId length is between 2 and 64 characters + const isLengthValid = accountId.length >= 2 && accountId.length <= 64; + if (!isLengthValid) return false; + // Check if accountId starts with '*.' || '*' remove for part verification + if (accountId.startsWith('*.')) accountId = accountId.slice(2); + if (accountId.startsWith('*')) accountId = accountId.slice(1); + + const parts = accountId.split('.'); + for (let part of parts) { + if (!part.match(/^[a-z\d]+([-_][a-z\d]+)*$/)) { + return false; + } + } + + return true; +}; + +const [checkBoxData, setCheckBoxData] = useState([]); +const [checkboxState, setCheckboxState] = useState(initialCheckboxState); +const [methodCount, setMethodCount] = useState(0); +const [contractInputMessage, setContractInputMessage] = useState(''); +const [inputValue, setInputValue] = useState(''); +const [allIndexers, setAllIndexers] = useState([]); +const [loading, setLoading] = useState(false); + +useEffect(() => { + Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { + const indexers = []; + Object.keys(data).forEach((accountId) => { + Object.keys(data[accountId]).forEach((functionName) => { + indexers.push({ + accountId: accountId, + indexerName: functionName, + }); + }); + }); + setAllIndexers(indexers) + }); +}); + +const handleFetchCheckboxData = async () => { + setCheckBoxData([]); + setMethodCount(0); + setContractInputMessage(''); + + if (!validateContractId(inputValue)) { + setContractInputMessage('Invalid contract id'); + return; + } + + setLoading(true); + + const url = 'https://europe-west1-pagoda-data-stack-prod.cloudfunctions.net/queryapi_wizard'; + asyncFetch(url, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + filter: inputValue, + }), + } + ) + .then(response => { + if (!response.ok) { + setError('There was an error fetching the data'); + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = response.body; + + if (data.length === 0) { + setContractInputMessage('No methods found for this contract'); + setLoading(false); + return; + }; + + setCheckBoxData(data); + setMethodCount(data.length); + setLoading(false); + }).catch(error => { + setLoading(false); + setError('There was an error fetching the data'); + }); + +}; + +const initialCheckboxState = checkBoxData.reduce((acc, item) => { + //Select eveyrthing by default. + acc[item.method_name] = false; + if (item.schema.properties) { + Object.keys(item.schema.properties).forEach(property => { + acc[`${item.method_name}::${property}`] = false; + }); + } + return acc; +}, {}); + +const handleParentChange = (methodName) => { + const newState = { ...checkboxState }; + const isChecked = !checkboxState[methodName]; + newState[methodName] = isChecked; + checkBoxData.forEach(item => { + if (item.method_name === methodName && item.schema.properties) { + Object.keys(item.schema.properties).forEach(property => { + newState[`${methodName}::${property}`] = isChecked; + }); + } + }); + setCheckboxState(newState); +}; + +const handleChildChange = (childId) => { + setCheckboxState({ + ...checkboxState, + [childId]: !checkboxState[childId], + }); +}; + +const data = allIndexers.map((indexer) => ({ + indexer: indexer.indexerName, + weeklyRequest: indexer.weeklyRequest || 150, + lastUpdated: indexer.lastUpdated || '2023-06-25', + status: indexer.status || 'Active', +})); + +function CustomTable() { + return ( + + + + + Indexer + Weekly Request + Last Updated + Status + + + + {data.map((row, index) => ( + + {row.indexer} + {row.weeklyRequest} + {row.lastUpdated} + {row.status} + + ))} + +
+
+ ); +} + + + +return ( + <> + Please note that this page is currently under development. Features may be incomplete or inaccurate + + + + Launch an indexer in minutes + Get a working indexer exportable to your Near react application faster than ever. Extract on-chain data, and easily query it using GraphQL endpoints and subscriptions. + + setInputValue(e.target.value)} + onKeyPress={(event) => event.key === 'Enter' && handleFetchCheckboxData()} + /> + Start + + {contractInputMessage ?? <> {contractInputMessage}} + + + + + Customize indexer + + {loading ? ( + + + + ) : (checkBoxData.length === 0) ? + <> + + + + No smart contract address entered + + + : ( +
+ {checkBoxData.length > 0 && ( + + Methods {methodCount} + + )} + < ScrollableDiv > + { + checkBoxData.length > 0 && ( + <> + {checkBoxData.map((item, index) => ( + + + handleParentChange(item.method_name)} + /> + {item.method_name} + + {item.schema.properties && ( + + {Object.keys(item.schema.properties).map((property, subIndex) => ( + + handleChildChange(`${item.method_name}::${property}`)} + /> + {property}: {item.schema.properties[property].type} + + ))} + + )} + + ))} + + ) + } + +
+ )} +
+
+
+
+
+ + + + Explore indexers on Near + + + + + + + + {CustomTable()} + + + +) From a39f835ec50fa72c2c8ac62ddc67ed7dad6ee219 Mon Sep 17 00:00:00 2001 From: Darun Seethammagari Date: Mon, 22 Jul 2024 22:08:08 +0530 Subject: [PATCH 03/10] refactor: Fetch Database Connection Parameters in Stream Handler (#890) Due to our migration of Provisioning logic to a separate API in Runner, we can expect that an Indexers resources will be initialized before we reach the point of executing blocks. So, it is no longer necessary to lazy init DmlHandler or IndexerMeta due to not having the connection parameters at the time of class initialization. Worker can poll for these params before even beginning to execute any blocks. This also continues to simplify both the code and behavior of the execute function. --- runner-client/examples/start_executor.rs | 17 +- runner/src/indexer/indexer.test.ts | 294 +++++++----------- runner/src/indexer/indexer.ts | 74 +---- .../services/runner/runner-service.test.ts | 25 +- .../server/services/runner/runner-service.ts | 8 +- runner/src/stream-handler/stream-handler.ts | 98 +++--- runner/src/stream-handler/worker.ts | 11 +- runner/tests/integration.test.ts | 58 ++-- 8 files changed, 265 insertions(+), 320 deletions(-) diff --git a/runner-client/examples/start_executor.rs b/runner-client/examples/start_executor.rs index 8764b4cd..4bb7a37b 100644 --- a/runner-client/examples/start_executor.rs +++ b/runner-client/examples/start_executor.rs @@ -9,15 +9,24 @@ async fn main() -> Result<(), Box> { let response = client .start_executor(Request::new(StartExecutorRequest { - account_id: "test_account".to_string(), - function_name: "test_indexer".to_string(), - code: "console.log('hi');".to_string(), + account_id: "account_near".to_string(), + function_name: "sample_indexer".to_string(), + code: " + console.log('Hello, world!'); + await context.db.IndexerStorage.insert({ + \"function_name\": \"sample_indexer\", + \"key_name\": block.blockHeight.toString(), + \"value\": \"Hello, world!\" + }); + " + .to_string(), schema: "CREATE TABLE \"indexer_storage\" ( \"function_name\" TEXT NOT NULL, \"key_name\" TEXT NOT NULL, \"value\" TEXT NOT NULL, PRIMARY KEY (\"function_name\", \"key_name\") - );".to_string(), + );" + .to_string(), redis_stream: "test:stream".to_string(), version: 123, })) diff --git a/runner/src/indexer/indexer.test.ts b/runner/src/indexer/indexer.test.ts index f2e4bad6..9de85774 100644 --- a/runner/src/indexer/indexer.test.ts +++ b/runner/src/indexer/indexer.test.ts @@ -7,10 +7,9 @@ import DmlHandler from '../dml-handler/dml-handler'; import type PgClient from '../pg-client'; import { LogLevel } from '../indexer-meta/log-entry'; import IndexerConfig from '../indexer-config/indexer-config'; -import type IndexerMeta from '../indexer-meta'; +import IndexerMeta from '../indexer-meta'; import { IndexerStatus } from '../indexer-meta'; -import type Provisioner from '../provisioner'; -import { type PostgresConnectionParams } from '../pg-client'; +import { PostgresConnectionParams } from '../pg-client'; describe('Indexer unit tests', () => { const SIMPLE_SCHEMA = `CREATE TABLE @@ -213,16 +212,9 @@ describe('Indexer unit tests', () => { const genericMockIndexerMeta: any = { writeLogs: jest.fn(), setStatus: jest.fn(), - updateBlockHeight: jest.fn() + updateBlockHeight: jest.fn().mockResolvedValue(null), } as unknown as IndexerMeta; - const genericProvisioner = { - getPgBouncerConnectionParameters: jest.fn().mockReturnValue(genericDbCredentials), - fetchUserApiProvisioningStatus: jest.fn().mockResolvedValue(true), - provisionLogsAndMetadataIfNeeded: jest.fn(), - ensureConsistentHasuraState: jest.fn(), - } as unknown as Provisioner; - const config = { hasuraEndpoint: 'mock-hasura-endpoint', hasuraAdminSecret: 'mock-hasura-secret', @@ -253,15 +245,14 @@ describe('Indexer unit tests', () => { const indexerMeta = { writeLogs: jest.fn(), setStatus: jest.fn(), - updateBlockHeight: jest.fn() + updateBlockHeight: jest.fn().mockResolvedValue(null), } as unknown as IndexerMeta; const indexerConfig = new IndexerConfig(SIMPLE_REDIS_STREAM, 'buildnear.testnet', 'test', 0, code, SIMPLE_SCHEMA, LogLevel.INFO); const indexer = new Indexer(indexerConfig, { fetch: mockFetch as unknown as typeof fetch, - provisioner: genericProvisioner, dmlHandler: genericMockDmlHandler, indexerMeta, - }, undefined, config); + }, config); await indexer.execute(mockBlock); @@ -292,8 +283,9 @@ describe('Indexer unit tests', () => { }); const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, - dmlHandler: genericMockDmlHandler - }, undefined, config); + dmlHandler: genericMockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); @@ -316,30 +308,30 @@ describe('Indexer unit tests', () => { expect(greet).toEqual('hello'); expect(success).toEqual(true); expect(mockFetch.mock.calls[0]).toEqual([ - `${config.hasuraEndpoint}/v1/graphql`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Hasura-Use-Backend-Only-Permissions': 'true', - 'X-Hasura-Role': 'morgs_near', - 'X-Hasura-Admin-Secret': config.hasuraAdminSecret - }, - body: JSON.stringify({ query }) - } + `${config.hasuraEndpoint}/v1/graphql`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Hasura-Use-Backend-Only-Permissions': 'true', + 'X-Hasura-Role': 'morgs_near', + 'X-Hasura-Admin-Secret': config.hasuraAdminSecret + }, + body: JSON.stringify({ query }) + } ]); expect(mockFetch.mock.calls[1]).toEqual([ - `${config.hasuraEndpoint}/v1/graphql`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Hasura-Use-Backend-Only-Permissions': 'true', - 'X-Hasura-Role': 'morgs_near', - 'X-Hasura-Admin-Secret': config.hasuraAdminSecret - }, - body: JSON.stringify({ query: mutation }) - } + `${config.hasuraEndpoint}/v1/graphql`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Hasura-Use-Backend-Only-Permissions': 'true', + 'X-Hasura-Role': 'morgs_near', + 'X-Hasura-Admin-Secret': config.hasuraAdminSecret + }, + body: JSON.stringify({ query: mutation }) + } ]); }); @@ -347,8 +339,9 @@ describe('Indexer unit tests', () => { const mockFetch = jest.fn(); const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, - dmlHandler: genericMockDmlHandler - }, undefined, config); + dmlHandler: genericMockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); @@ -377,7 +370,7 @@ describe('Indexer unit tests', () => { errors: ['boom'] }) }); - const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, dmlHandler: genericMockDmlHandler }, undefined, config); + const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }, config); const context = indexer.buildContext(1, []); @@ -392,7 +385,7 @@ describe('Indexer unit tests', () => { data: 'mock', }), }); - const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, dmlHandler: genericMockDmlHandler }, undefined, config); + const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }, config); const context = indexer.buildContext(1, []); @@ -401,25 +394,25 @@ describe('Indexer unit tests', () => { await context.graphql(query, variables); expect(mockFetch.mock.calls[0]).toEqual([ - `${config.hasuraEndpoint}/v1/graphql`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Hasura-Use-Backend-Only-Permissions': 'true', - 'X-Hasura-Role': 'morgs_near', - 'X-Hasura-Admin-Secret': config.hasuraAdminSecret - }, - body: JSON.stringify({ - query, - variables, - }), - }, + `${config.hasuraEndpoint}/v1/graphql`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Hasura-Use-Backend-Only-Permissions': 'true', + 'X-Hasura-Role': 'morgs_near', + 'X-Hasura-Admin-Secret': config.hasuraAdminSecret + }, + body: JSON.stringify({ + query, + variables, + }), + }, ]); }); test('GetTableNameToDefinitionNamesMapping works for a variety of input schemas', async () => { - const indexer = new Indexer(stressTestConfig); + const indexer = new Indexer(stressTestConfig, { dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }); const tableNameToDefinitionNamesMapping = indexer.getTableNameToDefinitionNamesMapping(STRESS_TEST_SCHEMA); expect([...tableNameToDefinitionNamesMapping.keys()]).toStrictEqual([ @@ -453,7 +446,7 @@ describe('Indexer unit tests', () => { }); test('GetTableNameToDefinitionNamesMapping works for mixed quotes schema', async () => { - const indexer = new Indexer(caseSensitiveConfig); + const indexer = new Indexer(caseSensitiveConfig, { dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }); const tableNameToDefinitionNamesMapping = indexer.getTableNameToDefinitionNamesMapping(CASE_SENSITIVE_SCHEMA); const tableNames = [...tableNameToDefinitionNamesMapping.keys()]; @@ -472,7 +465,7 @@ describe('Indexer unit tests', () => { }); test('GetSchemaLookup works for mixed quotes schema', async () => { - const indexer = new Indexer(caseSensitiveConfig); + const indexer = new Indexer(caseSensitiveConfig, { dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }); const schemaLookup = indexer.getTableNameToDefinitionNamesMapping(CASE_SENSITIVE_SCHEMA); const tableNames = [...schemaLookup.keys()]; @@ -489,7 +482,7 @@ describe('Indexer unit tests', () => { }); test('SanitizeTableName works properly on many test cases', async () => { - const indexer = new Indexer(simpleSchemaConfig, undefined, undefined, config); + const indexer = new Indexer(simpleSchemaConfig, { dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }, config); expect(indexer.sanitizeTableName('table_name')).toStrictEqual('TableName'); expect(indexer.sanitizeTableName('tablename')).toStrictEqual('Tablename'); // name is not capitalized @@ -512,7 +505,7 @@ describe('Indexer unit tests', () => { "id" SERIAL NOT NULL );`; const indexerConfig = new IndexerConfig(SIMPLE_REDIS_STREAM, SIMPLE_ACCOUNT_ID, SIMPLE_FUNCTION_NAME, 0, 'code', schemaWithDuplicateSanitizedTableNames, LogLevel.INFO); - const indexer = new Indexer(indexerConfig, { dmlHandler: genericMockDmlHandler }, undefined, config); + const indexer = new Indexer(indexerConfig, { dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }, config); // Does not outright throw an error but instead returns an empty object expect(indexer.buildDatabaseContext(1, [])) @@ -524,8 +517,9 @@ describe('Indexer unit tests', () => { const indexer = new Indexer(socialSchemaConfig, { fetch: genericMockFetch as unknown as typeof fetch, - dmlHandler: mockDmlHandler - }, genericDbCredentials, config); + dmlHandler: mockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); const objToInsert = [{ @@ -558,10 +552,11 @@ describe('Indexer unit tests', () => { const upsertSpy = jest.spyOn(mockDmlHandler, 'upsert'); const indexer = new Indexer(socialSchemaConfig, { fetch: genericMockFetch as unknown as typeof fetch, - dmlHandler: mockDmlHandler - }, genericDbCredentials, config); + dmlHandler: mockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); - const promises = []; + const promises: any[] = []; for (let i = 1; i <= 100; i++) { const promise = context.db.Posts.upsert( @@ -593,8 +588,9 @@ describe('Indexer unit tests', () => { const indexer = new Indexer(socialSchemaConfig, { fetch: genericMockFetch as unknown as typeof fetch, - dmlHandler: mockDmlHandler - }, genericDbCredentials, config); + dmlHandler: mockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); const objToSelect = { @@ -619,8 +615,9 @@ describe('Indexer unit tests', () => { const indexer = new Indexer(socialSchemaConfig, { fetch: genericMockFetch as unknown as typeof fetch, - dmlHandler: mockDmlHandler - }, genericDbCredentials, config); + dmlHandler: mockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); const whereObj = { @@ -649,8 +646,9 @@ describe('Indexer unit tests', () => { const indexer = new Indexer(socialSchemaConfig, { fetch: genericMockFetch as unknown as typeof fetch, - dmlHandler: mockDmlHandler - }, genericDbCredentials, config); + dmlHandler: mockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); const objToInsert = [{ @@ -681,8 +679,9 @@ describe('Indexer unit tests', () => { const indexer = new Indexer(socialSchemaConfig, { fetch: genericMockFetch as unknown as typeof fetch, - dmlHandler: mockDmlHandler - }, genericDbCredentials, config); + dmlHandler: mockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); const deleteFilter = { @@ -696,8 +695,9 @@ describe('Indexer unit tests', () => { test('indexer builds context and verifies all methods generated', async () => { const indexer = new Indexer(stressTestConfig, { fetch: genericMockFetch as unknown as typeof fetch, - dmlHandler: genericMockDmlHandler - }, genericDbCredentials, config); + dmlHandler: genericMockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); expect(Object.keys(context.db)).toStrictEqual([ @@ -735,8 +735,9 @@ describe('Indexer unit tests', () => { const indexerConfig = new IndexerConfig(SIMPLE_REDIS_STREAM, SIMPLE_ACCOUNT_ID, SIMPLE_FUNCTION_NAME, 0, 'code', '', LogLevel.INFO); const indexer = new Indexer(indexerConfig, { fetch: genericMockFetch as unknown as typeof fetch, - dmlHandler: genericMockDmlHandler - }, genericDbCredentials, config); + dmlHandler: genericMockDmlHandler, + indexerMeta: genericMockIndexerMeta, + }, config); const context = indexer.buildContext(1, []); expect(Object.keys(context.db)).toStrictEqual([]); @@ -838,10 +839,9 @@ describe('Indexer unit tests', () => { const indexerConfig = new IndexerConfig(SIMPLE_REDIS_STREAM, 'buildnear.testnet', 'test', 0, code, SIMPLE_SCHEMA, LogLevel.INFO); const indexer = new Indexer(indexerConfig, { fetch: mockFetch as unknown as typeof fetch, - provisioner: genericProvisioner, dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta - }, undefined, config); + }, config); await indexer.execute(mockBlock); @@ -892,15 +892,14 @@ describe('Indexer unit tests', () => { const indexerMeta = { writeLogs: jest.fn(), setStatus: jest.fn(), - updateBlockHeight: jest.fn() + updateBlockHeight: jest.fn().mockResolvedValue(null) } as unknown as IndexerMeta; const indexerConfig = new IndexerConfig(SIMPLE_REDIS_STREAM, 'buildnear.testnet', 'test', 0, code, SIMPLE_SCHEMA, LogLevel.INFO); const indexer = new Indexer(indexerConfig, { fetch: mockFetch as unknown as typeof fetch, - provisioner: genericProvisioner, dmlHandler: genericMockDmlHandler, indexerMeta, - }, undefined, config); + }, config); await expect(indexer.execute(mockBlock)).rejects.toThrow(new Error('Execution error: boom')); expect(mockFetch.mock.calls).toMatchSnapshot(); @@ -909,56 +908,6 @@ describe('Indexer unit tests', () => { expect(indexerMeta.updateBlockHeight).not.toHaveBeenCalled(); }); - test('Indexer.execute() skips database credentials fetch second time onward', async () => { - const blockHeight = 82699904; - const mockFetch = jest.fn(() => ({ - status: 200, - json: async () => ({ - errors: null, - }), - })); - const mockBlock = Block.fromStreamerMessage({ - block: { - chunks: [0], - header: { - height: blockHeight - } - }, - shards: {} - } as unknown as StreamerMessage) as unknown as Block; - const provisioner: any = { - getPgBouncerConnectionParameters: jest.fn().mockReturnValue(genericDbCredentials), - fetchUserApiProvisioningStatus: jest.fn().mockReturnValue(true), - provisionUserApi: jest.fn(), - provisionLogsAndMetadataIfNeeded: jest.fn(), - ensureConsistentHasuraState: jest.fn(), - }; - const indexerMeta = { - writeLogs: jest.fn(), - setStatus: jest.fn(), - updateBlockHeight: jest.fn() - } as unknown as IndexerMeta; - const indexer = new Indexer(simpleSchemaConfig, { - fetch: mockFetch as unknown as typeof fetch, - provisioner, - dmlHandler: genericMockDmlHandler, - indexerMeta, - }, undefined, config); - - await indexer.execute(mockBlock); - await indexer.execute(mockBlock); - await indexer.execute(mockBlock); - - expect(provisioner.provisionUserApi).not.toHaveBeenCalled(); - expect(provisioner.getPgBouncerConnectionParameters).toHaveBeenCalledTimes(1); - // expect(provisioner.provisionLogsAndMetadataIfNeeded).toHaveBeenCalled(); - // expect(provisioner.ensureConsistentHasuraState).toHaveBeenCalled(); - expect(indexerMeta.setStatus).toHaveBeenCalledTimes(1); // Status is cached, so only called once - expect(indexerMeta.setStatus).toHaveBeenCalledWith(IndexerStatus.RUNNING); - expect(indexerMeta.updateBlockHeight).toHaveBeenCalledTimes(3); - expect(indexerMeta.updateBlockHeight).toHaveBeenCalledWith(blockHeight); - }); - test('Indexer.execute() supplies the required role to the GraphQL endpoint', async () => { const blockHeight = 82699904; const mockFetch = jest.fn(() => ({ @@ -976,17 +925,10 @@ describe('Indexer unit tests', () => { }, shards: {} } as unknown as StreamerMessage) as unknown as Block; - const provisioner: any = { - getPgBouncerConnectionParameters: jest.fn().mockReturnValue(genericDbCredentials), - fetchUserApiProvisioningStatus: jest.fn().mockReturnValue(true), - provisionUserApi: jest.fn(), - provisionLogsAndMetadataIfNeeded: jest.fn(), - ensureConsistentHasuraState: jest.fn(), - }; const indexerMeta = { writeLogs: jest.fn(), setStatus: jest.fn(), - updateBlockHeight: jest.fn() + updateBlockHeight: jest.fn().mockResolvedValue(null) } as unknown as IndexerMeta; const code = ` context.graphql(\`mutation { set(functionName: "buildnear.testnet/test", key: "height", data: "\${block.blockHeight}")}\`); @@ -994,19 +936,14 @@ describe('Indexer unit tests', () => { const indexerConfig = new IndexerConfig(SIMPLE_REDIS_STREAM, 'morgs.near', 'test', 0, code, SIMPLE_SCHEMA, LogLevel.INFO); const indexer = new Indexer(indexerConfig, { fetch: mockFetch as unknown as typeof fetch, - provisioner, dmlHandler: genericMockDmlHandler, indexerMeta, - }, undefined, config); + }, config); await indexer.execute(mockBlock); - expect(provisioner.provisionUserApi).not.toHaveBeenCalled(); expect(indexerMeta.setStatus).toHaveBeenNthCalledWith(1, IndexerStatus.RUNNING); expect(mockFetch.mock.calls).toMatchSnapshot(); - expect(provisioner.getPgBouncerConnectionParameters).toHaveBeenCalledTimes(1); - // expect(provisioner.provisionLogsAndMetadataIfNeeded).toHaveBeenCalledTimes(1); - // expect(provisioner.ensureConsistentHasuraState).toHaveBeenCalledTimes(1); expect(indexerMeta.updateBlockHeight).toHaveBeenCalledWith(blockHeight); }); @@ -1014,17 +951,17 @@ describe('Indexer unit tests', () => { const mockDebugIndexerMeta = { writeLogs: jest.fn(), setStatus: jest.fn(), - updateBlockHeight: jest.fn() + updateBlockHeight: jest.fn().mockResolvedValue(null) }; const mockInfoIndexerMeta = { writeLogs: jest.fn(), setStatus: jest.fn(), - updateBlockHeight: jest.fn() + updateBlockHeight: jest.fn().mockResolvedValue(null) }; const mockErrorIndexerMeta = { writeLogs: jest.fn(), setStatus: jest.fn(), - updateBlockHeight: jest.fn() + updateBlockHeight: jest.fn().mockResolvedValue(null) }; const blockHeight = 456; const mockBlock = Block.fromStreamerMessage({ @@ -1055,33 +992,27 @@ describe('Indexer unit tests', () => { debugIndexerConfig, { fetch: jest.fn() as unknown as typeof fetch, - provisioner: genericProvisioner, dmlHandler: mockDmlHandler, indexerMeta: mockDebugIndexerMeta as unknown as IndexerMeta }, - undefined, config ); const indexerInfo = new Indexer( infoIndexerConfig, { fetch: jest.fn() as unknown as typeof fetch, - provisioner: genericProvisioner, dmlHandler: mockDmlHandler, indexerMeta: mockInfoIndexerMeta as unknown as IndexerMeta }, - undefined, config ); const indexerError = new Indexer( errorIndexerConfig, { fetch: jest.fn() as unknown as typeof fetch, - provisioner: genericProvisioner, dmlHandler: mockDmlHandler, indexerMeta: mockErrorIndexerMeta as unknown as IndexerMeta }, - undefined, config ); @@ -1113,7 +1044,7 @@ describe('Indexer unit tests', () => { data: {} }) }); - const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch }, undefined, config); + const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }, config); const context = indexer.buildContext(1, []); const mutation = ` @@ -1127,17 +1058,17 @@ describe('Indexer unit tests', () => { await context.graphql(mutation); expect(mockFetch.mock.calls[0]).toEqual([ - `${config.hasuraEndpoint}/v1/graphql`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Hasura-Use-Backend-Only-Permissions': 'true', - 'X-Hasura-Role': simpleSchemaConfig.hasuraRoleName(), - 'X-Hasura-Admin-Secret': config.hasuraAdminSecret - }, - body: JSON.stringify({ query: mutation }) - } + `${config.hasuraEndpoint}/v1/graphql`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Hasura-Use-Backend-Only-Permissions': 'true', + 'X-Hasura-Role': simpleSchemaConfig.hasuraRoleName(), + 'X-Hasura-Admin-Secret': config.hasuraAdminSecret + }, + body: JSON.stringify({ query: mutation }) + } ]); }); @@ -1162,7 +1093,7 @@ describe('Indexer unit tests', () => { const indexerMeta: any = { writeLogs: jest.fn(), setStatus: jest.fn(), - updateBlockHeight: jest.fn(), + updateBlockHeight: jest.fn().mockResolvedValue(null), }; const code = ` @@ -1179,8 +1110,7 @@ describe('Indexer unit tests', () => { const mockDmlHandler: DmlHandler = { select: jest.fn() } as unknown as DmlHandler; const indexerDebug = new Indexer( debugIndexerConfig, - { fetch: mockFetchDebug as unknown as typeof fetch, provisioner: genericProvisioner, dmlHandler: mockDmlHandler, indexerMeta }, - undefined, + { fetch: mockFetchDebug as unknown as typeof fetch, dmlHandler: mockDmlHandler, indexerMeta }, config ); @@ -1196,7 +1126,7 @@ describe('Indexer unit tests', () => { data: {} }) }); - const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, dmlHandler: genericMockDmlHandler }, undefined, config); + const indexer = new Indexer(simpleSchemaConfig, { fetch: mockFetch as unknown as typeof fetch, dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }, config); const mutation = ` mutation { @@ -1209,21 +1139,21 @@ describe('Indexer unit tests', () => { await indexer.runGraphQLQuery(mutation, null, 0, null); expect(mockFetch.mock.calls[0]).toEqual([ - `${config.hasuraEndpoint}/v1/graphql`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Hasura-Use-Backend-Only-Permissions': 'true', - }, - body: JSON.stringify({ query: mutation }) - } + `${config.hasuraEndpoint}/v1/graphql`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-Hasura-Use-Backend-Only-Permissions': 'true', + }, + body: JSON.stringify({ query: mutation }) + } ]); }); test('transformedCode applies the correct transformations', () => { const indexerConfig = new IndexerConfig(SIMPLE_REDIS_STREAM, SIMPLE_ACCOUNT_ID, SIMPLE_FUNCTION_NAME, 0, 'console.log(\'hello\')', SIMPLE_SCHEMA, LogLevel.INFO); - const indexer = new Indexer(indexerConfig, { dmlHandler: genericMockDmlHandler }, undefined, config); + const indexer = new Indexer(indexerConfig, { dmlHandler: genericMockDmlHandler, indexerMeta: genericMockIndexerMeta }, config); const transformedFunction = indexer.transformIndexerFunction(); expect(transformedFunction).toEqual(` diff --git a/runner/src/indexer/indexer.ts b/runner/src/indexer/indexer.ts index 1fd154ef..9b827051 100644 --- a/runner/src/indexer/indexer.ts +++ b/runner/src/indexer/indexer.ts @@ -6,20 +6,18 @@ import { trace, type Span } from '@opentelemetry/api'; import VError from 'verror'; import logger from '../logger'; -import Provisioner from '../provisioner'; -import DmlHandler from '../dml-handler/dml-handler'; +import type DmlHandler from '../dml-handler/dml-handler'; import LogEntry from '../indexer-meta/log-entry'; import type IndexerConfig from '../indexer-config'; -import { type PostgresConnectionParams } from '../pg-client'; -import IndexerMeta, { IndexerStatus } from '../indexer-meta'; +import type IndexerMeta from '../indexer-meta'; +import { IndexerStatus } from '../indexer-meta'; import { wrapSpan } from '../utility'; interface Dependencies { - fetch: typeof fetch - provisioner: Provisioner - dmlHandler?: DmlHandler - indexerMeta?: IndexerMeta - parser: Parser + fetch?: typeof fetch + dmlHandler: DmlHandler + indexerMeta: IndexerMeta + parser?: Parser }; interface Context { @@ -55,14 +53,12 @@ export default class Indexer { tracer = trace.getTracer('queryapi-runner-indexer'); private readonly logger: typeof logger; - private readonly deps: Dependencies; - private database_connection_parameters: PostgresConnectionParams | undefined; + private readonly deps: Required; private currentStatus?: string; constructor ( private readonly indexerConfig: IndexerConfig, - deps?: Partial, - databaseConnectionParameters: PostgresConnectionParams | undefined = undefined, + deps: Dependencies, private readonly config: Config = defaultConfig, ) { this.logger = logger.child({ accountId: indexerConfig.accountId, functionName: indexerConfig.functionName, service: this.constructor.name }); @@ -70,11 +66,9 @@ export default class Indexer { this.DEFAULT_HASURA_ROLE = 'append'; this.deps = { fetch, - provisioner: new Provisioner(), parser: new Parser(), - ...deps, + ...deps }; - this.database_connection_parameters = databaseConnectionParameters; } async execute ( @@ -92,20 +86,7 @@ export default class Indexer { try { const runningMessage = `Running function ${this.indexerConfig.fullName()} on block ${blockHeight}, lag is: ${lag?.toString()}ms from block timestamp`; - logEntries.push(LogEntry.systemInfo(runningMessage, blockHeight)); - // Cache database credentials after provisioning - await wrapSpan(async () => { - try { - this.database_connection_parameters ??= await this.deps.provisioner.getPgBouncerConnectionParameters(this.indexerConfig.hasuraRoleName()); - this.deps.indexerMeta ??= new IndexerMeta(this.indexerConfig, this.database_connection_parameters); - this.deps.dmlHandler ??= new DmlHandler(this.database_connection_parameters, this.indexerConfig); - } catch (e) { - const error = e as Error; - logEntries.push(LogEntry.systemError(`Failed to get database connection parameters: ${error.message}`, blockHeight)); - throw error; - } - }, this.tracer, 'get database connection parameters'); const resourceCreationSpan = this.tracer.startSpan('prepare vm and context to run indexer code'); simultaneousPromises.push(this.setStatus(IndexerStatus.RUNNING).catch((e: Error) => { @@ -133,7 +114,7 @@ export default class Indexer { runIndexerCodeSpan.end(); } }); - simultaneousPromises.push(this.updateIndexerBlockHeight(blockHeight).catch((e: Error) => { + simultaneousPromises.push(this.deps.indexerMeta.updateBlockHeight(blockHeight).catch((e: Error) => { this.logger.error('Failed to update block height', e); })); } catch (e) { @@ -143,7 +124,7 @@ export default class Indexer { })); throw e; } finally { - const results = await Promise.allSettled([(this.deps.indexerMeta as IndexerMeta).writeLogs(logEntries), ...simultaneousPromises]); + const results = await Promise.allSettled([(this.deps.indexerMeta).writeLogs(logEntries), ...simultaneousPromises]); if (this.IS_FIRST_EXECUTION && results[0].status === 'rejected') { this.logger.error('Failed to write logs after executing on block:', results[0].reason); } @@ -278,7 +259,7 @@ export default class Indexer { const tableNameToDefinitionNamesMapping = this.getTableNameToDefinitionNamesMapping(this.indexerConfig.schema); const tableNames = Array.from(tableNameToDefinitionNamesMapping.keys()); const sanitizedTableNames = new Set(); - const dmlHandler: DmlHandler = this.deps.dmlHandler as DmlHandler; + const dmlHandler: DmlHandler = this.deps.dmlHandler; // Generate and collect methods for each table name const result = tableNames.reduce((prev, tableName) => { @@ -351,35 +332,6 @@ export default class Indexer { await this.deps.indexerMeta?.setStatus(status); } - private async createIndexerMetaIfNotExists (failureMessage: string): Promise { - if (!this.deps.indexerMeta) { - try { - this.database_connection_parameters ??= await this.deps.provisioner.getPgBouncerConnectionParameters(this.indexerConfig.hasuraRoleName()); - this.deps.indexerMeta = new IndexerMeta(this.indexerConfig, this.database_connection_parameters); - } catch (e) { - const error = e as Error; - this.logger.error(failureMessage, e); - throw error; - } - } - } - - async setStoppedStatus (): Promise { - await this.createIndexerMetaIfNotExists(`${this.indexerConfig.fullName()}: Failed to get DB params to set status STOPPED for stream`); - const indexerMeta: IndexerMeta = this.deps.indexerMeta as IndexerMeta; - await indexerMeta.setStatus(IndexerStatus.STOPPED); - } - - async writeCrashedWorkerLog (logEntry: LogEntry): Promise { - await this.createIndexerMetaIfNotExists(`${this.indexerConfig.fullName()}: Failed to get DB params to write crashed worker error log for stream`); - const indexerMeta: IndexerMeta = this.deps.indexerMeta as IndexerMeta; - await indexerMeta.writeLogs([logEntry]); - } - - async updateIndexerBlockHeight (blockHeight: number): Promise { - await (this.deps.indexerMeta as IndexerMeta).updateBlockHeight(blockHeight); - } - async runGraphQLQuery (operation: string, variables: any, blockHeight: number, hasuraRoleName: string | null, logError: boolean = true): Promise { const response: Response = await this.deps.fetch(`${this.config.hasuraEndpoint}/v1/graphql`, { method: 'POST', diff --git a/runner/src/server/services/runner/runner-service.test.ts b/runner/src/server/services/runner/runner-service.test.ts index 57ff6de6..8f8126bf 100644 --- a/runner/src/server/services/runner/runner-service.test.ts +++ b/runner/src/server/services/runner/runner-service.test.ts @@ -26,6 +26,7 @@ describe('Runner gRPC Service', () => { genericStreamHandlerType = jest.fn().mockImplementation((indexerConfig) => { return { indexerConfig, + start: jest.fn().mockResolvedValue(null), stop: jest.fn(), }; }); @@ -168,11 +169,14 @@ describe('Runner gRPC Service', () => { }); it('stops a executor with correct settings', (done) => { + const start = jest.fn().mockImplementation(async () => { + await Promise.resolve(); + }); const stop = jest.fn().mockImplementation(async () => { await Promise.resolve(); }); const streamHandlerType = jest.fn().mockImplementation((indexerConfig) => { - return { stop, indexerConfig }; + return { start, stop, indexerConfig }; }); const service = getRunnerService(new Map(), streamHandlerType); const mockCallback = jest.fn() as unknown as any; @@ -195,11 +199,14 @@ describe('Runner gRPC Service', () => { }); it('Invalid stop executor request with empty executor Id', () => { + const start = jest.fn().mockImplementation(async () => { + await Promise.resolve(); + }); const stop = jest.fn().mockImplementation(async () => { await Promise.resolve(); }); const streamHandlerType = jest.fn().mockImplementation(() => { - return { stop }; + return { start, stop }; }); const service = getRunnerService(new Map(), streamHandlerType); const mockCallback = jest.fn() as unknown as any; @@ -215,11 +222,14 @@ describe('Runner gRPC Service', () => { }); it('Invalid stop executor request with non-existent executor', (done) => { + const start = jest.fn().mockImplementation(async () => { + await Promise.resolve(); + }); const stop = jest.fn().mockImplementation(async () => { await Promise.resolve(); }); const streamHandlerType = jest.fn().mockImplementation(() => { - return { stop }; + return { start, stop }; }); const service = getRunnerService(new Map(), streamHandlerType); @@ -235,11 +245,14 @@ describe('Runner gRPC Service', () => { }); it('Invalid stop executor request with somehow failing stop', (done) => { + const start = jest.fn().mockImplementation(async () => { + await Promise.resolve(); + }); const stop = jest.fn().mockImplementation(async () => { await Promise.reject(new Error('somehow fails')); }); const streamHandlerType = jest.fn().mockImplementation((indexerConfig) => { - return { stop, indexerConfig }; + return { start, stop, indexerConfig }; }); const service = getRunnerService(new Map(), streamHandlerType); const mockCallback = jest.fn() as unknown as any; @@ -262,11 +275,15 @@ describe('Runner gRPC Service', () => { }); it('valid list executor request lists executors correctly, with stopped indexer', async () => { + const start = jest.fn().mockImplementation(async () => { + await Promise.resolve(); + }); const stop = jest.fn().mockImplementation(async () => { await Promise.resolve(); }); const streamHandlerType = jest.fn().mockImplementation((indexerConfig) => { return { + start, stop, indexerConfig, executorContext: BASIC_EXECUTOR_CONTEXT diff --git a/runner/src/server/services/runner/runner-service.ts b/runner/src/server/services/runner/runner-service.ts index 3b45856e..6299beac 100644 --- a/runner/src/server/services/runner/runner-service.ts +++ b/runner/src/server/services/runner/runner-service.ts @@ -14,6 +14,7 @@ import { type StopExecutorResponse__Output, type StopExecutorResponse } from '.. import { type ListExecutorsRequest__Output } from '../../../generated/runner/ListExecutorsRequest'; import { type ListExecutorsResponse__Output, type ListExecutorsResponse } from '../../../generated/runner/ListExecutorsResponse'; import { type ExecutorInfo__Output } from '../../../generated/runner/ExecutorInfo'; +import { type ExecutionState__Output } from '../../../generated/runner/ExecutionState'; export function getRunnerService ( executors: Map = new Map(), @@ -33,7 +34,7 @@ export function getRunnerService ( functionName: executor.indexerConfig.functionName, version: executor.indexerConfig.version.toString(), health: { - executionState: executor.executorContext.executionState, + executionState: executor.executorContext.executionState as ExecutionState__Output } }); } else { @@ -79,6 +80,9 @@ export function getRunnerService ( const streamHandler = new StreamHandlerType(indexerConfig); executors.set(indexerConfig.executorId, streamHandler); callback(null, { executorId: indexerConfig.executorId }); + streamHandler.start().catch((error: Error) => { + logger.error('Failed to start executor', error); + }); } catch (e) { const error = e as Error; @@ -153,7 +157,7 @@ export function getRunnerService ( functionName: indexerConfig.functionName, version: indexerConfig.version.toString(), health: { - executionState: indexerContext.executionState, + executionState: indexerContext.executionState as ExecutionState__Output } }); }); diff --git a/runner/src/stream-handler/stream-handler.ts b/runner/src/stream-handler/stream-handler.ts index f1b24b4f..b6742840 100644 --- a/runner/src/stream-handler/stream-handler.ts +++ b/runner/src/stream-handler/stream-handler.ts @@ -2,16 +2,19 @@ import path from 'path'; import { Worker, isMainThread } from 'worker_threads'; import { registerWorkerMetrics, deregisterWorkerMetrics } from '../metrics'; -import Indexer from '../indexer'; import LogEntry from '../indexer-meta/log-entry'; import logger from '../logger'; import type IndexerConfig from '../indexer-config'; +import IndexerMeta, { IndexerStatus } from '../indexer-meta'; +import assert from 'assert'; +import Provisioner from '../provisioner'; +import { type PostgresConnectionParams } from '../pg-client'; export enum WorkerMessageType { - METRICS = 'METRICS', - BLOCK_HEIGHT = 'BLOCK_HEIGHT', - EXECUTION_STATE = 'STATUS', + METRICS, + BLOCK_HEIGHT, + EXECUTION_STATE, } export interface WorkerMessage { @@ -20,11 +23,13 @@ export interface WorkerMessage { } export enum ExecutionState { + UNSPECIFIED = 'UNSPECIFIED', RUNNING = 'RUNNING', FAILING = 'FAILING', WAITING = 'WAITING', STOPPED = 'STOPPED', STALLED = 'STALLED', + IDLE = 'IDLE', } interface ExecutorContext { @@ -34,59 +39,79 @@ interface ExecutorContext { export default class StreamHandler { private readonly logger: typeof logger; - private readonly worker: Worker; + private worker: Worker | undefined; public readonly executorContext: ExecutorContext; + private indexerMeta: IndexerMeta | undefined; constructor ( public readonly indexerConfig: IndexerConfig, ) { - if (isMainThread) { - this.logger = logger.child({ accountId: indexerConfig.accountId, functionName: indexerConfig.functionName, service: this.constructor.name }); + this.logger = logger.child({ accountId: indexerConfig.accountId, functionName: indexerConfig.functionName, service: this.constructor.name }); - this.worker = new Worker(path.join(__dirname, 'worker.js'), { - workerData: { - indexerConfigData: indexerConfig.toObject(), - }, - }); - this.executorContext = { - executionState: ExecutionState.RUNNING, - block_height: indexerConfig.version, - }; + this.executorContext = { + executionState: ExecutionState.IDLE, + block_height: indexerConfig.version, + }; + } - this.worker.on('message', this.handleMessage.bind(this)); - this.worker.on('error', this.handleError.bind(this)); + async start (): Promise { + if (isMainThread) { + try { + const provisioner = new Provisioner(); + const databaseConnectionParams: PostgresConnectionParams = await provisioner.getPgBouncerConnectionParameters(this.indexerConfig.hasuraRoleName()); + + this.indexerMeta = new IndexerMeta(this.indexerConfig, databaseConnectionParams); + this.worker = new Worker(path.join(__dirname, 'worker.js'), { + workerData: { + indexerConfigData: this.indexerConfig.toObject(), + databaseConnectionParams, + }, + }); + + this.worker.on('message', this.handleMessage.bind(this)); + this.worker.on('error', this.handleError.bind(this)); + + this.executorContext.executionState = ExecutionState.RUNNING; + } catch (error: any) { + const errorContent = error instanceof Error ? error.toString() : JSON.stringify(error); + this.logger.error('Terminating thread', error); + this.executorContext.executionState = ExecutionState.STALLED; + throw new Error(`Failed to start Indexer: ${errorContent}`); + } } else { - throw new Error('StreamHandler should not be instantiated in a worker thread'); + throw new Error('StreamHandler should not be started in a worker thread'); } } async stop (): Promise { - deregisterWorkerMetrics(this.worker.threadId); - + if (this.worker) { + deregisterWorkerMetrics(this.worker.threadId); + await this.worker.terminate(); + } this.executorContext.executionState = ExecutionState.STOPPED; - - await this.worker.terminate(); } private handleError (error: Error): void { this.logger.error('Terminating thread', error); this.executorContext.executionState = ExecutionState.STALLED; - const indexer = new Indexer(this.indexerConfig); - indexer.setStoppedStatus().catch((e) => { - this.logger.error('Failed to set stopped status for indexer', e); - }); - const errorContent = error instanceof Error ? error.toString() : JSON.stringify(error); - const streamErrorLogEntry = LogEntry.systemError(`Encountered error processing stream: ${this.indexerConfig.redisStreamKey}, terminating thread\n${errorContent}`, this.executorContext.block_height); - - indexer.writeCrashedWorkerLog(streamErrorLogEntry) - .catch((e) => { - this.logger.error('Failed to write failure log for stream', e); + if (this.indexerMeta) { + this.indexerMeta.setStatus(IndexerStatus.STOPPED).catch((e: Error) => { + this.logger.error('Failed to set stopped status for indexer', e); }); + const errorContent = error instanceof Error ? error.toString() : JSON.stringify(error); + const streamErrorLogEntry = LogEntry.systemError(`Encountered error processing stream: ${this.indexerConfig.redisStreamKey}, terminating thread\n${errorContent}`, this.executorContext.block_height); + this.indexerMeta.writeLogs([streamErrorLogEntry]) + .catch((e) => { + this.logger.error('Failed to write failure log for stream', e); + }); + } - this.worker.terminate().catch(() => { - this.logger.error('Failed to terminate thread for stream'); - }); + if (this.worker) { + this.worker.terminate().catch(() => { + this.logger.error('Failed to terminate thread for stream'); + }); + } } private handleMessage (message: WorkerMessage): void { @@ -98,6 +123,7 @@ export default class StreamHandler { this.executorContext.block_height = message.data; break; case WorkerMessageType.METRICS: + assert(this.worker, 'Worker is not initialized'); registerWorkerMetrics(this.worker.threadId, message.data); break; } diff --git a/runner/src/stream-handler/worker.ts b/runner/src/stream-handler/worker.ts index fb1a897c..34b78351 100644 --- a/runner/src/stream-handler/worker.ts +++ b/runner/src/stream-handler/worker.ts @@ -9,9 +9,12 @@ import { METRICS } from '../metrics'; import LakeClient from '../lake-client'; import { WorkerMessageType, type WorkerMessage, ExecutionState } from './stream-handler'; import setUpTracerExport from '../instrumentation'; +import IndexerMeta from '../indexer-meta/indexer-meta'; import IndexerConfig from '../indexer-config'; import parentLogger from '../logger'; import { wrapSpan } from '../utility'; +import { type PostgresConnectionParams } from '../pg-client'; +import DmlHandler from '../dml-handler/dml-handler'; if (isMainThread) { throw new Error('Worker should not be run on main thread'); @@ -29,6 +32,7 @@ interface WorkerContext { lakeClient: LakeClient queue: PrefetchQueue indexerConfig: IndexerConfig + databaseConnectionParams: PostgresConnectionParams logger: typeof parentLogger } @@ -51,6 +55,7 @@ void (async function main () { lakeClient: new LakeClient(), queue: [], indexerConfig, + databaseConnectionParams: workerData.databaseConnectionParams, logger }; @@ -95,7 +100,11 @@ async function blockQueueProducer (workerContext: WorkerContext): Promise async function blockQueueConsumer (workerContext: WorkerContext): Promise { let previousError: string = ''; const indexerConfig: IndexerConfig = workerContext.indexerConfig; - const indexer = new Indexer(indexerConfig); + + const dmlHandler: DmlHandler = new DmlHandler(workerContext.databaseConnectionParams, indexerConfig); + const indexerMeta: IndexerMeta = new IndexerMeta(indexerConfig, workerContext.databaseConnectionParams); + const indexer = new Indexer(indexerConfig, { dmlHandler, indexerMeta }); + let streamMessageId = ''; let currBlockHeight = 0; diff --git a/runner/tests/integration.test.ts b/runner/tests/integration.test.ts index 3a74fbc8..7b2112b0 100644 --- a/runner/tests/integration.test.ts +++ b/runner/tests/integration.test.ts @@ -13,6 +13,8 @@ import block_115185108 from './blocks/00115185108/streamer_message.json'; import block_115185109 from './blocks/00115185109/streamer_message.json'; import { LogLevel } from '../src/indexer-meta/log-entry'; import IndexerConfig from '../src/indexer-config'; +import IndexerMeta from '../src/indexer-meta/indexer-meta'; +import DmlHandler from '../src/dml-handler/dml-handler'; describe('Indexer integration', () => { jest.setTimeout(300_000); @@ -114,19 +116,7 @@ describe('Indexer integration', () => { LogLevel.INFO ); - const indexer = new Indexer( - indexerConfig, - { - provisioner - }, - undefined, - { - hasuraAdminSecret: hasuraContainer.getAdminSecret(), - hasuraEndpoint: hasuraContainer.getEndpoint(), - } - ); - - await provisioner.provisionUserApi(indexerConfig); + const indexer = await prepareIndexer(indexerConfig, provisioner, hasuraContainer); await indexer.execute(Block.fromStreamerMessage(block_115185108 as any as StreamerMessage)); @@ -222,19 +212,7 @@ describe('Indexer integration', () => { LogLevel.INFO ); - const indexer = new Indexer( - indexerConfig, - { - provisioner - }, - undefined, - { - hasuraAdminSecret: hasuraContainer.getAdminSecret(), - hasuraEndpoint: hasuraContainer.getEndpoint(), - } - ); - - await provisioner.provisionUserApi(indexerConfig); + const indexer = await prepareIndexer(indexerConfig, provisioner, hasuraContainer); await indexer.execute(Block.fromStreamerMessage(block_115185108 as any as StreamerMessage)); await indexer.execute(Block.fromStreamerMessage(block_115185109 as any as StreamerMessage)); @@ -293,7 +271,27 @@ describe('Indexer integration', () => { }); }); -async function indexerLogsQuery (indexerSchemaName: string, graphqlClient: GraphQLClient): Promise { +async function prepareIndexer(indexerConfig: IndexerConfig, provisioner: Provisioner, hasuraContainer: StartedHasuraGraphQLContainer): Promise { + await provisioner.provisionUserApi(indexerConfig); + + const db_connection_params = await provisioner.getPostgresConnectionParameters(indexerConfig.userName()); + const dmlHandler = new DmlHandler(db_connection_params, indexerConfig); + const indexerMeta = new IndexerMeta(indexerConfig, db_connection_params); + + return new Indexer( + indexerConfig, + { + dmlHandler, + indexerMeta, + }, + { + hasuraAdminSecret: hasuraContainer.getAdminSecret(), + hasuraEndpoint: hasuraContainer.getEndpoint(), + } + ); +} + +async function indexerLogsQuery(indexerSchemaName: string, graphqlClient: GraphQLClient): Promise { const graphqlResult: any = await graphqlClient.request(gql` query { ${indexerSchemaName}_sys_logs { @@ -304,15 +302,15 @@ async function indexerLogsQuery (indexerSchemaName: string, graphqlClient: Graph return graphqlResult[`${indexerSchemaName}_sys_logs`]; } -async function indexerStatusQuery (indexerSchemaName: string, graphqlClient: GraphQLClient): Promise { +async function indexerStatusQuery(indexerSchemaName: string, graphqlClient: GraphQLClient): Promise { return await indexerMetadataQuery(indexerSchemaName, 'STATUS', graphqlClient); } -async function indexerBlockHeightQuery (indexerSchemaName: string, graphqlClient: GraphQLClient): Promise { +async function indexerBlockHeightQuery(indexerSchemaName: string, graphqlClient: GraphQLClient): Promise { return await indexerMetadataQuery(indexerSchemaName, 'LAST_PROCESSED_BLOCK_HEIGHT', graphqlClient); } -async function indexerMetadataQuery (indexerSchemaName: string, attribute: string, graphqlClient: GraphQLClient): Promise { +async function indexerMetadataQuery(indexerSchemaName: string, attribute: string, graphqlClient: GraphQLClient): Promise { const graphqlResult: any = await graphqlClient.request(gql` query { ${indexerSchemaName}_sys_metadata(where: {attribute: {_eq: "${attribute}"}}) { From b29d8b63e5f171a9e68aa531d5b791b6dd9a6631 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <42101107+Kevin101Zhang@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:53:47 -0400 Subject: [PATCH 04/10] feat: converted wizard dummy file to ts, added additional testcases (#911) converted the generateCode dummy file to TS and added additional testcases. --- .../Editor/EditorComponents/GenerateCode.jsx | 124 +++++----- frontend/src/pages/api/generateCode.js | 71 ------ frontend/src/pages/api/generateCode.ts | 98 ++++++++ frontend/src/pages/query-api-editor/index.js | 6 +- frontend/src/test/api/generateCode.test.js | 217 ++++++++++++++---- frontend/src/utils/pgSchemaTypeGen.js | 6 +- 6 files changed, 334 insertions(+), 188 deletions(-) delete mode 100644 frontend/src/pages/api/generateCode.js create mode 100644 frontend/src/pages/api/generateCode.ts diff --git a/frontend/src/components/Editor/EditorComponents/GenerateCode.jsx b/frontend/src/components/Editor/EditorComponents/GenerateCode.jsx index c83d8bcd..dedc6f63 100644 --- a/frontend/src/components/Editor/EditorComponents/GenerateCode.jsx +++ b/frontend/src/components/Editor/EditorComponents/GenerateCode.jsx @@ -2,71 +2,71 @@ import { useState } from 'react'; const GenerateCode = () => { - const [contractFilter, setContractFilter] = useState(''); - const [selectedMethods, setSelectedMethods] = useState([]); - const [selectedEvents, setSelectedEvents] = useState([]); - const [generatedCode, setGeneratedCode] = useState({ jsCode: '', sqlCode: '' }); + const [contractFilter, setContractFilter] = useState(''); + const [selectedMethods, setSelectedMethods] = useState([]); + const [selectedEvents, setSelectedEvents] = useState([]); + const [generatedCode, setGeneratedCode] = useState({ jsCode: '', sqlCode: '' }); - const handleGenerateCode = async () => { - const response = await fetch('/api/generateCode', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ contractFilter, selectedMethods, selectedEvents }), - }); - const data = await response.json(); - setGeneratedCode(data); - }; + const handleGenerateCode = async () => { + const response = await fetch('/api/generateCode', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ contractFilter, selectedMethods, selectedEvents }), + }); + const data = await response.json(); + setGeneratedCode(data); + }; - return ( -
-
-

Generate Code

-
- setContractFilter(e.target.value)} - className="w-full p-3 border border-gray-300 rounded-md" - /> -
-
- setSelectedMethods(e.target.value.split(','))} - className="w-full p-3 border border-gray-300 rounded-md" - /> -
-
- setSelectedEvents(e.target.value.split(','))} - className="w-full p-3 border border-gray-300 rounded-md" - /> -
- -
-

Generated JavaScript Code

-
{generatedCode.jsCode}
-
-
-

Generated SQL Code

-
{generatedCode.sqlCode}
-
-
+ return ( +
+
+

Generate Code

+
+ setContractFilter(e.target.value)} + className="w-full p-3 border border-gray-300 rounded-md" + />
- ); +
+ setSelectedMethods(e.target.value.split(','))} + className="w-full p-3 border border-gray-300 rounded-md" + /> +
+
+ setSelectedEvents(e.target.value.split(','))} + className="w-full p-3 border border-gray-300 rounded-md" + /> +
+ +
+

Generated JavaScript Code

+
{generatedCode.jsCode}
+
+
+

Generated SQL Code

+
{generatedCode.sqlCode}
+
+
+
+ ); }; export default GenerateCode; diff --git a/frontend/src/pages/api/generateCode.js b/frontend/src/pages/api/generateCode.js deleted file mode 100644 index 44d121ce..00000000 --- a/frontend/src/pages/api/generateCode.js +++ /dev/null @@ -1,71 +0,0 @@ -import { defaultCode, defaultSchema, } from '../../utils/formatters'; - -export default function handler(req, res) { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Allow-Methods', 'POST'); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - - if (req.method === 'OPTIONS') { - res.status(200).end(); - return; - } - - const { contractFilter, selectedMethods, selectedEvents } = req.body; - - if (!contractFilter || !selectedMethods || !selectedEvents) { - return res.status(400).json({ error: 'Missing required fields' }); - } - - if (!Array.isArray(selectedMethods) || !Array.isArray(selectedEvents)) { - return res.status(400).json({ error: 'selectedMethods and selectedEvents must be arrays' }); - } - - const jsCode = generateDummyJSCode(contractFilter, selectedMethods, selectedEvents); - const sqlCode = generateDummySQLCode(contractFilter, selectedMethods, selectedEvents); - - res.status(200).json({ jsCode, sqlCode }); -} - -function generateDummyJSCode(contractFilter, selectedMethods, selectedEvents) { - let jsCode = `// JavaScript Code\n\n`; - jsCode += `-- Contract Filter: ${contractFilter}\n\n`; - jsCode += `-- Selected Methods: ${selectedMethods}\n\n`; - jsCode += `-- Selected Events: ${selectedEvents}\n\n`; - - jsCode += defaultCode; - - selectedMethods.forEach(method => { - jsCode += `function ${method}() {\n`; - jsCode += ` console.log('Executing ${method}');\n`; - jsCode += `}\n\n`; - }); - - selectedEvents.forEach(event => { - jsCode += `function handle${event}() {\n`; - jsCode += ` console.log('Handling event ${event}');\n`; - jsCode += `}\n\n`; - }); - - return jsCode; -} - -function generateDummySQLCode(contractFilter, selectedMethods, selectedEvents) { - let sqlCode = `-- SQL Code\n\n`; - sqlCode += `-- Contract Filter: ${contractFilter}\n\n`; - sqlCode += `-- Selected Methods: ${selectedMethods}\n\n`; - sqlCode += `-- Selected Events: ${selectedEvents}\n\n`; - - sqlCode += defaultSchema; - - selectedMethods.forEach(method => { - sqlCode += `-- Method: ${method}\n`; - sqlCode += `INSERT INTO methods (name) VALUES ('${method}');\n\n`; - }); - - selectedEvents.forEach(event => { - sqlCode += `-- Event: ${event}\n`; - sqlCode += `INSERT INTO events (name) VALUES ('${event}');\n\n`; - }); - - return sqlCode; -} diff --git a/frontend/src/pages/api/generateCode.ts b/frontend/src/pages/api/generateCode.ts new file mode 100644 index 00000000..50c0d8bd --- /dev/null +++ b/frontend/src/pages/api/generateCode.ts @@ -0,0 +1,98 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; + +import { defaultCode, defaultSchema } from '../../utils/formatters'; + +interface RequestBody { + contractFilter: string | string[]; + selectedMethods: string[]; + selectedEvents: string[]; +} + +const validateRequestBody = (body: any): body is RequestBody => { + const isStringOrArray = (value: any): value is string | string[] => + typeof value === 'string' || (Array.isArray(value) && value.every((item) => typeof item === 'string')); + + return ( + isStringOrArray(body.contractFilter) && + Array.isArray(body.selectedMethods) && + body.selectedMethods.every((method: any) => typeof method === 'string') && + Array.isArray(body.selectedEvents) && + body.selectedEvents.every((event: any) => typeof event === 'string') + ); +}; + +const generateDummyJSCode = ( + contractFilter: string | string[], + selectedMethods: string[], + selectedEvents: string[], +): string => { + const filterString = Array.isArray(contractFilter) ? contractFilter.join(', ') : contractFilter; + const jsCodeHeader = + `// JavaScript Code\n\n` + + `-- Contract Filter: ${filterString}\n\n` + + `-- Selected Methods: ${selectedMethods.join(', ')}\n\n` + + `-- Selected Events: ${selectedEvents.join(', ')}\n\n`; + + const methodsJS = selectedMethods + .map((method) => `function ${method}() {\n console.log('Executing ${method}');\n}\n\n`) + .join(''); + + const eventsJS = selectedEvents + .map((event) => `function handle${event}() {\n console.log('Handling event ${event}');\n}\n\n`) + .join(''); + + return jsCodeHeader + defaultCode + methodsJS + eventsJS; +}; + +const generateDummySQLCode = ( + contractFilter: string | string[], + selectedMethods: string[], + selectedEvents: string[], +): string => { + const filterString = Array.isArray(contractFilter) ? contractFilter.join(', ') : contractFilter; + const sqlCodeHeader = + `-- SQL Code\n\n` + + `-- Contract Filter: ${filterString}\n\n` + + `-- Selected Methods: ${selectedMethods.join(', ')}\n\n` + + `-- Selected Events: ${selectedEvents.join(', ')}\n\n`; + + const methodsSQL = selectedMethods + .map((method) => `-- Method: ${method}\nINSERT INTO methods (name) VALUES ('${method}');\n\n`) + .join(''); + + const eventsSQL = selectedEvents + .map((event) => `-- Event: ${event}\nINSERT INTO events (name) VALUES ('${event}');\n\n`) + .join(''); + + return sqlCodeHeader + defaultSchema + methodsSQL + eventsSQL; +}; + +export default function handler(req: NextApiRequest, res: NextApiResponse): void { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'POST'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + + if (req.method === 'OPTIONS') { + res.status(200).end(); + return; + } + if (req.method !== 'POST') { + res.status(405).json({ error: 'Method Not Allowed' }); + return; + } + + if (!validateRequestBody(req.body)) { + res.status(400).json({ + error: + 'Invalid request body: contractFilter must be a string or an array of strings, and selectedMethods and selectedEvents must be arrays of strings', + }); + return; + } + + const { contractFilter, selectedMethods, selectedEvents } = req.body; + + const jsCode = generateDummyJSCode(contractFilter, selectedMethods, selectedEvents); + const sqlCode = generateDummySQLCode(contractFilter, selectedMethods, selectedEvents); + + res.status(200).json({ jsCode, sqlCode }); +} diff --git a/frontend/src/pages/query-api-editor/index.js b/frontend/src/pages/query-api-editor/index.js index 32e2d2d1..87750c60 100644 --- a/frontend/src/pages/query-api-editor/index.js +++ b/frontend/src/pages/query-api-editor/index.js @@ -3,9 +3,9 @@ import React, { useContext, useEffect } from 'react'; import { Alert } from 'react-bootstrap'; import Editor from '@/components/Editor/EditorComponents/Editor'; +import GenerateCode from '@/components/Editor/EditorComponents/GenerateCode'; import IndexerLogsContainer from '@/components/Logs/LogsViewContainer/IndexerLogsContainer'; import { IndexerDetailsContext } from '@/contexts/IndexerDetailsContext'; -import GenerateCode from '@/components/Editor/EditorComponents/GenerateCode'; const QueryApiEditorPage = ({ router }) => { const { accountId, indexerName } = router.query; @@ -26,9 +26,7 @@ const QueryApiEditorPage = ({ router }) => { } if (accountId == 'test' || indexerName == 'test') { - return ( - - ); + return ; } return showLogsView ? : ; diff --git a/frontend/src/test/api/generateCode.test.js b/frontend/src/test/api/generateCode.test.js index ff3156f0..4fa52f28 100644 --- a/frontend/src/test/api/generateCode.test.js +++ b/frontend/src/test/api/generateCode.test.js @@ -1,66 +1,189 @@ -import handler from '../../pages/api/generateCode'; import { createMocks } from 'node-mocks-http'; -jest.mock('../../utils/formatters', () => ({ - defaultCode: '// Default JS Code', - defaultSchema: '-- Default SQL Schema', -})); +import handler from '../../pages/api/generateCode'; describe('API Handler', () => { - it('should return generated JS and SQL code for valid input', async () => { - const { req, res } = createMocks({ - method: 'POST', - body: { - contractFilter: 'filter', - selectedMethods: ['method1', 'method2'], - selectedEvents: ['event1', 'event2'], - }, - }); + it('should return generated JS and SQL code for valid input', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { + contractFilter: 'filter', + selectedMethods: ['method1', 'method2'], + selectedEvents: ['event1', 'event2'], + }, + }); + + await handler(req, res); + + expect(res._getStatusCode()).toBe(200); + const responseData = JSON.parse(res._getData()); + expect(responseData).toHaveProperty('jsCode'); + expect(responseData).toHaveProperty('sqlCode'); + }); + + it('should return 400 if required fields are missing', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: {}, + }); + + await handler(req, res); + + expect(res._getStatusCode()).toBe(400); + expect(JSON.parse(res._getData())).toEqual({ + error: + 'Invalid request body: contractFilter must be a string or an array of strings, and selectedMethods and selectedEvents must be arrays of strings', + }); + }); + + it('should return 400 if selectedMethods or selectedEvents are not arrays', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { + contractFilter: 'filter', + selectedMethods: 'not-an-array', + selectedEvents: [], + }, + }); + + await handler(req, res); + + expect(res._getStatusCode()).toBe(400); + expect(JSON.parse(res._getData())).toEqual({ + error: + 'Invalid request body: contractFilter must be a string or an array of strings, and selectedMethods and selectedEvents must be arrays of strings', + }); + + const { req: req2, res: res2 } = createMocks({ + method: 'POST', + body: { + contractFilter: 'filter', + selectedMethods: [], + selectedEvents: 'not-an-array', + }, + }); + + await handler(req2, res2); - await handler(req, res); + expect(res2._getStatusCode()).toBe(400); + expect(JSON.parse(res2._getData())).toEqual({ + error: + 'Invalid request body: contractFilter must be a string or an array of strings, and selectedMethods and selectedEvents must be arrays of strings', + }); + }); + + it('should handle OPTIONS request correctly', async () => { + const { req, res } = createMocks({ + method: 'OPTIONS', + }); + + await handler(req, res); + + expect(res._getStatusCode()).toBe(200); + }); + + it('should handle empty arrays correctly', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { + contractFilter: 'filter', + selectedMethods: [], + selectedEvents: [], + }, + }); + + await handler(req, res); + + expect(res._getStatusCode()).toBe(200); + const responseData = JSON.parse(res._getData()); + expect(responseData).toHaveProperty('jsCode'); + expect(responseData).toHaveProperty('sqlCode'); + expect(responseData.jsCode).toContain('// JavaScript Code'); + expect(responseData.sqlCode).toContain('-- SQL Code'); + }); - expect(res._getStatusCode()).toBe(200); + it('should return 400 for invalid contractFilter data type', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { + contractFilter: 123, + selectedMethods: ['method1'], + selectedEvents: ['event1'], + }, }); - it('should return 400 if required fields are missing', async () => { - const { req, res } = createMocks({ - method: 'POST', - body: {}, - }); + await handler(req, res); - await handler(req, res); + expect(res._getStatusCode()).toBe(400); + expect(JSON.parse(res._getData())).toEqual({ + error: + 'Invalid request body: contractFilter must be a string or an array of strings, and selectedMethods and selectedEvents must be arrays of strings', + }); + }); - expect(res._getStatusCode()).toBe(400); - expect(JSON.parse(res._getData())).toEqual({ error: 'Missing required fields' }); + it('should return 400 for invalid method or event data types', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { + contractFilter: 'filter', + selectedMethods: [123], + selectedEvents: ['event1'], + }, }); - it('should return 400 if selectedMethods or selectedEvents are not arrays', async () => { - const { req, res } = createMocks({ - method: 'POST', - body: { - contractFilter: 'filter', - selectedMethods: 'not-an-array', - selectedEvents: [], - }, - }); + await handler(req, res); - await handler(req, res); + expect(res._getStatusCode()).toBe(400); + expect(JSON.parse(res._getData())).toEqual({ + error: + 'Invalid request body: contractFilter must be a string or an array of strings, and selectedMethods and selectedEvents must be arrays of strings', + }); + }); - expect(res._getStatusCode()).toBe(400); - expect(JSON.parse(res._getData())).toEqual({ error: 'selectedMethods and selectedEvents must be arrays' }); + it('should handle large inputs correctly', async () => { + const largeArray = Array.from({ length: 1000 }, (_, i) => `method${i}`); + const { req, res } = createMocks({ + method: 'POST', + body: { + contractFilter: 'filter', + selectedMethods: largeArray, + selectedEvents: largeArray, + }, + }); - const { req: req2, res: res2 } = createMocks({ - method: 'POST', - body: { - contractFilter: 'filter', - selectedMethods: [], - selectedEvents: 'not-an-array', - }, - }); + await handler(req, res); - await handler(req2, res2); + expect(res._getStatusCode()).toBe(200); + const responseData = JSON.parse(res._getData()); + expect(responseData).toHaveProperty('jsCode'); + expect(responseData).toHaveProperty('sqlCode'); + }); - expect(res2._getStatusCode()).toBe(400); - expect(JSON.parse(res2._getData())).toEqual({ error: 'selectedMethods and selectedEvents must be arrays' }); + it('should return 405 for unsupported HTTP methods', async () => { + const { req, res } = createMocks({ + method: 'GET', }); + + await handler(req, res); + + expect(res._getStatusCode()).toBe(405); + expect(JSON.parse(res._getData())).toEqual({ error: 'Method Not Allowed' }); + }); + + it('should have correct CORS headers', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { + contractFilter: 'filter', + selectedMethods: ['method1'], + selectedEvents: ['event1'], + }, + }); + + await handler(req, res); + + expect(res._getHeaders()).toHaveProperty('access-control-allow-origin', '*'); + expect(res._getHeaders()).toHaveProperty('access-control-allow-methods', 'POST'); + expect(res._getHeaders()).toHaveProperty('access-control-allow-headers', 'Content-Type'); + }); }); diff --git a/frontend/src/utils/pgSchemaTypeGen.js b/frontend/src/utils/pgSchemaTypeGen.js index a53df000..d4e4a4fb 100644 --- a/frontend/src/utils/pgSchemaTypeGen.js +++ b/frontend/src/utils/pgSchemaTypeGen.js @@ -103,9 +103,7 @@ export class PgSchemaTypeGen { Object.prototype.hasOwnProperty.call(columnSpec, 'definition') ) { this.addColumn(columnSpec, columns); - } else if ( - columnSpec.constraint_type === 'primary key' - ) { + } else if (columnSpec.constraint_type === 'primary key') { for (const foreignKeyDef of columnSpec.definition) { columns[foreignKeyDef.column.expr.value].nullable = false; } @@ -211,7 +209,7 @@ export class PgSchemaTypeGen { const tsType = columnDetails.nullable ? columnDetails.type + ' | null' : columnDetails.type; const optional = columnDetails.required ? '' : '?'; - queryDefinition += ` ${columnName}?: ${tsType} | ${tsType}[];\n` + queryDefinition += ` ${columnName}?: ${tsType} | ${tsType}[];\n`; itemDefinition += ` ${columnName}?: ${tsType};\n`; inputDefinition += ` ${columnName}${optional}: ${tsType};\n`; } From c3439c34f045b5641a93fa72b102381d5c7291e2 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <42101107+Kevin101Zhang@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:28:17 -0400 Subject: [PATCH 05/10] feat: use QueryAPI Indexer for Explorers and added pagination (#907) QueryAPI Indexer's explore page now fetches from Indexers and will default to NEAR RPC request if something goes wrong. Added Pagination/Loading more for All Indexers --- frontend/src/pages/query-api-editor/index.js | 2 +- .../widgets/src/QueryApi.IndexerExplorer.jsx | 301 +++++++++++++----- 2 files changed, 227 insertions(+), 76 deletions(-) diff --git a/frontend/src/pages/query-api-editor/index.js b/frontend/src/pages/query-api-editor/index.js index 87750c60..ec622a31 100644 --- a/frontend/src/pages/query-api-editor/index.js +++ b/frontend/src/pages/query-api-editor/index.js @@ -25,7 +25,7 @@ const QueryApiEditorPage = ({ router }) => { ); } - if (accountId == 'test' || indexerName == 'test') { + if (accountId == 'test' && indexerName == 'test') { return ; } diff --git a/frontend/widgets/src/QueryApi.IndexerExplorer.jsx b/frontend/widgets/src/QueryApi.IndexerExplorer.jsx index abb9ca61..1562f8f4 100644 --- a/frontend/widgets/src/QueryApi.IndexerExplorer.jsx +++ b/frontend/widgets/src/QueryApi.IndexerExplorer.jsx @@ -1,33 +1,135 @@ const myAccountId = context.accountId; +const PAGE_SIZE = 50; +const TABLE_NAME = "dataplatform_near_queryapi_indexer_indexers"; +const GET_ALL_ACTIVE_INDEXERS = ` +query getAllActiveIndexers($limit: Int!, $offset: Int!) { + ${TABLE_NAME}( + where: {is_removed: {_eq: false}} + limit: $limit + offset: $offset + ) { + author_account_id + indexer_name + } + ${TABLE_NAME}_aggregate( + where: {is_removed: {_eq: false}} + ) { + aggregate { + count + } + } +} +`; + +const GET_MY_ACTIVE_INDEXERS = ` +query getMyActiveIndexers($authorAccountId: String!) { + ${TABLE_NAME}( + where: { + is_removed: { _eq: false } + author_account_id: { _eq: $authorAccountId } + } + ) { + author_account_id + indexer_name + } +} +`; + const [selectedTab, setSelectedTab] = useState(props.tab && props.tab !== "all" ? props.tab : "all"); -const [myIndexers, setMyIndexers] = useState([]); -const [allIndexers, setAllIndexers] = useState([]); -const [error, setError] = useState(null); const [indexerMetadata, setIndexerMetaData] = useState(new Map()); + +const [indexers, setIndexers] = useState([]); +const [total, setTotal] = useState(0); +const [myIndexers, setMyIndexers] = useState([]); +const [page, setPage] = useState(0); + const [loading, setLoading] = useState(false); +const [isLoadingMore, setIsLoadingMore] = useState(false); +const [isFetching, setIsFetching] = useState(false); +const [error, setError] = useState(null); + +const fetchGraphQL = (operationsDoc, operationName, variables) => { + const graphQLEndpoint = `${REPL_GRAPHQL_ENDPOINT}`; + return asyncFetch(`${graphQLEndpoint}/v1/graphql`, { + method: "POST", + headers: { + "x-hasura-role": "dataplatform_near", + }, + body: JSON.stringify({ + query: operationsDoc, + variables: variables, + operationName: operationName, + }), + }); +} -const fetchIndexerData = () => { +const fetchMyIndexerData = () => { setLoading(true); - Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { - const allIndexers = []; - const myIndexers = []; - Object.keys(data).forEach((accId) => { - Object.keys(data[accId]).forEach((functionName) => { - const indexer = { - accountId: accId, - indexerName: functionName, - }; - if (accId === myAccountId) myIndexers.push(indexer); - allIndexers.push(indexer); - }); + + fetchGraphQL(GET_MY_ACTIVE_INDEXERS, 'getMyActiveIndexers', { + authorAccountId: myAccountId, + }) + .then((result) => { + if (result.status === 200) { + const data = result?.body?.data?.[TABLE_NAME]; + if (Array.isArray(data)) { + const newIndexers = data.map(({ author_account_id, indexer_name }) => ({ + accountId: author_account_id, + indexerName: indexer_name, + })); + setMyIndexers(newIndexers); + } else { + throw new Error('Data is not an array:', data); + } + } else { + throw new Error('Failed to fetch data:', result); + } + setLoading(false); + }) + .catch((error) => { + setError('An error occurred while retrieving indexer data. Attempting to fetch from NEAR RPC...', error); + backupNearRPCRequest(); }); - setMyIndexers(myIndexers); - setAllIndexers(allIndexers); - setLoading(false); - }); } +const fetchIndexerData = (page, append) => { + if (isFetching) return; + setIsFetching(true); + append ? setIsLoadingMore(true) : setLoading(true); + + fetchGraphQL(GET_ALL_ACTIVE_INDEXERS, 'getAllActiveIndexers', { + limit: PAGE_SIZE, + offset: page * PAGE_SIZE, + }) + .then((result) => { + if (result.status === 200) { + const data = result?.body?.data?.[TABLE_NAME]; + const totalCount = result?.body?.data?.[`${TABLE_NAME}_aggregate`]?.aggregate?.count; + if (Array.isArray(data)) { + const newIndexers = data.map(({ author_account_id, indexer_name }) => ({ + accountId: author_account_id, + indexerName: indexer_name, + })); + setTotal(totalCount); + setIndexers((prevIndexers) => append ? [...prevIndexers, ...newIndexers] : newIndexers); + } else { + throw new Error('Data is not an array:', data); + } + } else { + throw new Error('Failed to fetch data:', result); + } + append ? setIsLoadingMore(false) : setLoading(false); + setIsFetching(false); + }) + .catch((error) => { + setError('An error occurred while retrieving indexer data. Attempting to fetch from NEAR RPC...', error); + append ? setIsLoadingMore(false) : setLoading(false); + setIsFetching(false); + backupNearRPCRequest(); + }); +}; + const storeIndexerMetaData = () => { const url = `${REPL_QUERY_API_USAGE_URL}`; @@ -35,7 +137,7 @@ const storeIndexerMetaData = () => { .then(response => { if (!response.ok) { setError('There was an error fetching the data'); - throw new Error(`HTTP error! status: ${response.status}`); + return; } const { data } = JSON.parse(response.body); const map = new Map(); @@ -55,15 +157,55 @@ const storeIndexerMetaData = () => { }); }); setIndexerMetaData(map); - setError(null); }) } useEffect(() => { - fetchIndexerData(); storeIndexerMetaData(); }, []); +useEffect(() => { + fetchIndexerData(page, page > 0); +}, [page]); + +useEffect(() => { + if (selectedTab === "my-indexers") { + if (myIndexers.length <= 0) fetchMyIndexerData(); + } + if (selectedTab === "all") { + if (indexers.length <= 0) fetchIndexerData(page, page > 0); + } +}, [selectedTab]); + +const handleLoadMore = () => { + if (!isLoadingMore) setPage((prevPage) => prevPage + 1); +}; + +const backupNearRPCRequest = () => { + console.log('Retrieving data from Near RPC..'); + setLoading(true); + Near.asyncView(`${REPL_REGISTRY_CONTRACT_ID}`, "list_all").then((data) => { + const allIndexers = []; + const myIndexers = []; + + Object.keys(data).forEach((accId) => { + Object.keys(data[accId]).forEach((functionName) => { + const indexer = { + accountId: accId, + indexerName: functionName, + }; + if (accId === myAccountId) myIndexers.push(indexer); + allIndexers.push(indexer); + }); + }); + setMyIndexers(myIndexers); + setIndexers(allIndexers); + setTotal(0); + setLoading(false); + setError(null); + }); +} + const Container = styled.div` display: flex; justify-content: center; @@ -176,6 +318,13 @@ const Button = styled.button` color: #11181c !important; margin: 0; + &:disabled { + background-color: #cccccc; + color: #666666; + cursor: not-allowed; + opacity: 0.6; + } + &:hover, &:focus { background: #ecedee; @@ -186,6 +335,8 @@ const Button = styled.button` span { color: #687076 !important; } + + `; const Tabs = styled.div` @@ -290,7 +441,7 @@ const SignUpLink = styled.a` color: #0070f3; text-decoration: none; font-size: 0.75rem; - margin-right: 1rem; + margin-left: 1rem; `; const ButtonWrapper = styled.div` @@ -356,6 +507,14 @@ const ToggleButton = styled.button` } `; +const LoadMoreContainer = styled.div` + display: flex; + justify-content: center; + align-items: flex-end; + margin-top: 16px; + padding: 16px; +`; + const LoadingSpinner = () => { const spinnerStyle = { width: '40px', @@ -391,56 +550,40 @@ const LoadingSpinner = () => { return ( - - - - QueryApi - + + setSelectedTab("my-indexers")} + selected={selectedTab === "my-indexers"} + > + My Indexers + + setSelectedTab("all")} + selected={selectedTab === "all"} + > + All Indexers + + (Documentation) - - - { - setActiveTab("create-new-indexer"); - setSelectedIndexerName(""); - selectTab("create-new-indexer"); - }} - > - Create New Indexer - - - - setSelectedTab("my-indexers")} - selected={selectedTab === "my-indexers"} - > - My Indexers - - setSelectedTab("all")} - selected={selectedTab === "all"} + + { + setActiveTab("create-new-indexer"); + setSelectedIndexerName(""); + selectTab("create-new-indexer"); + }} > - All Indexers - - + Create New Indexer + + {error && {error}} @@ -452,16 +595,24 @@ return ( ) : ( - - {allIndexers.map((indexer, i) => ( - - - - ))} - + <> + + {indexers.map((indexer, i) => ( + + + + ))} + + {isLoadingMore && } + + + + )} )} From 38ac863fbb51806ba40f48ce13ff3476a00b01be Mon Sep 17 00:00:00 2001 From: Darun Seethammagari Date: Wed, 24 Jul 2024 05:15:39 +0530 Subject: [PATCH 06/10] feat: Add metric for receiver block backfill failures (#912) Adds a metric for tracking receiver block backfill status. Since the backfill has an end state, I can't rely on constantly incrementing a counter. Instead, I use a gauge and check for non zero values. If the backfill starts and completes without issue, the value will be 0. But, if the backfill fails, it will increment the gauge and then skip to Lake backfill, leaving the gauge at a nonzero value. We can alert on this. If the stream is restarted, then a successful attempt at the backfill will reset the gauge to 0. --- block-streamer/src/block_stream.rs | 8 ++++++++ block-streamer/src/graphql/client.rs | 6 ++++++ block-streamer/src/metrics.rs | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/block-streamer/src/block_stream.rs b/block-streamer/src/block_stream.rs index eff2a0d1..a9af3f10 100644 --- a/block-streamer/src/block_stream.rs +++ b/block-streamer/src/block_stream.rs @@ -409,9 +409,14 @@ async fn process_bitmap_indexer_blocks( let mut last_published_block_height: u64 = start_block_height; + let indexer_name = indexer.get_full_name(); + while let Some(block_height_result) = matching_block_heights.next().await { match block_height_result { Ok(block_height) => { + metrics::RECEIVER_BLOCKS_FAILURE + .with_label_values(&[&indexer_name]) + .set(0); redis .publish_block(indexer, redis_stream.clone(), block_height, MAX_STREAM_SIZE) .await?; @@ -422,6 +427,9 @@ async fn process_bitmap_indexer_blocks( last_published_block_height = block_height; } Err(err) => { + metrics::RECEIVER_BLOCKS_FAILURE + .with_label_values(&[&indexer_name]) + .inc(); tracing::error!( "Backfill using bitmap indexer failed unexpectedly: {:?}", err diff --git a/block-streamer/src/graphql/client.rs b/block-streamer/src/graphql/client.rs index 38aad637..516f2b3c 100644 --- a/block-streamer/src/graphql/client.rs +++ b/block-streamer/src/graphql/client.rs @@ -57,6 +57,12 @@ impl GraphQLClientImpl { .json(&body) .send() .await?; + if reqwest_response.status() != 200 { + tracing::error!( + "GraphQL query failed with status code: {}", + reqwest_response.status() + ); + } reqwest_response.json().await } diff --git a/block-streamer/src/metrics.rs b/block-streamer/src/metrics.rs index 1f01a071..ec166e83 100644 --- a/block-streamer/src/metrics.rs +++ b/block-streamer/src/metrics.rs @@ -63,6 +63,12 @@ lazy_static! { &["indexer"] ) .unwrap(); + pub static ref RECEIVER_BLOCKS_FAILURE: IntGaugeVec = register_int_gauge_vec!( + "queryapi_block_streamer_receiver_blocks_failure", + "Gauge which only has a nonzero value if an error occurs during receiver block backfill", + &["indexer"] + ) + .unwrap(); } pub struct LogCounter; From 2add1729f3166f09da7c5e222ba1888ac01022b5 Mon Sep 17 00:00:00 2001 From: Darun Seethammagari Date: Wed, 24 Jul 2024 06:20:12 +0530 Subject: [PATCH 07/10] feat: Programmatically Execute Indexer Locally (#908) This PR sets up a local indexer class which leverages the in memory dml handler class and a no op indexer meta class to fully execute indexer logic locally on a block. The class is responsible for downloading block data and providing helper functions to abstract testing logic away. This PR also sets up a core-indexers folder which is used to verify that the created local indexer is actually capable of executing indexer code locally. The core-indexers folder may be temporary as we do intend to eventually publish a testing framework npm package. For the time being, it uses relative imports to work. --- core-indexers/.eslintrc.js | 35 + core-indexers/jest.config.js | 6 + core-indexers/package-lock.json | 6752 +++++++++++++++++ core-indexers/package.json | 33 + core-indexers/receiver-blocks/indexer.js | 324 + core-indexers/receiver-blocks/schema.sql | 20 + core-indexers/receiver-blocks/unit.test.ts | 20 + core-indexers/tsconfig.json | 25 + runner/.eslintrc.js | 1 + runner/package-lock.json | 1 + runner/package.json | 2 +- runner/protos/runner.proto | 2 + runner/src/dml-handler/dml-handler.ts | 4 +- .../src/dml-handler/in-memory-dml-handler.ts | 12 +- runner/src/indexer-config/indexer-config.ts | 30 + runner/src/indexer-meta/indexer-meta.ts | 14 +- runner/src/indexer-meta/no-op-indexer-meta.ts | 32 + runner/src/indexer/indexer.test.ts | 4 +- runner/src/indexer/indexer.ts | 21 +- runner/src/lake-client/lake-client.test.ts | 88 +- runner/src/lake-client/lake-client.ts | 14 +- runner/src/local-indexer/index.ts | 1 + runner/src/local-indexer/local-indexer.ts | 33 + runner/src/stream-handler/worker.ts | 13 +- 24 files changed, 7365 insertions(+), 122 deletions(-) create mode 100644 core-indexers/.eslintrc.js create mode 100644 core-indexers/jest.config.js create mode 100644 core-indexers/package-lock.json create mode 100644 core-indexers/package.json create mode 100644 core-indexers/receiver-blocks/indexer.js create mode 100644 core-indexers/receiver-blocks/schema.sql create mode 100644 core-indexers/receiver-blocks/unit.test.ts create mode 100644 core-indexers/tsconfig.json create mode 100644 runner/src/indexer-meta/no-op-indexer-meta.ts create mode 100644 runner/src/local-indexer/index.ts create mode 100644 runner/src/local-indexer/local-indexer.ts diff --git a/core-indexers/.eslintrc.js b/core-indexers/.eslintrc.js new file mode 100644 index 00000000..4c29ffd9 --- /dev/null +++ b/core-indexers/.eslintrc.js @@ -0,0 +1,35 @@ +module.exports = { + parser: '@typescript-eslint/parser', + env: { + es2021: true, + node: true + }, + overrides: [ + { + files: ['.eslintrc.js', 'jest.config.js'], + parser: 'espree', + extends: ['standard'], + rules: { + semi: ['error', 'always'], + quotes: ['error', 'single'], + 'array-callback-return': ['error', { allowImplicit: false }] + } + }, + { + files: ['**/*.ts'], + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname + }, + extends: [ + 'standard-with-typescript' + ], + rules: { + '@typescript-eslint/semi': ['error', 'always'], + '@typescript-eslint/comma-dangle': ['error', 'only-multiline'], + '@typescript-eslint/strict-boolean-expressions': 'off', + 'array-callback-return': ['error', { allowImplicit: false }] + } + } + ] +}; diff --git a/core-indexers/jest.config.js b/core-indexers/jest.config.js new file mode 100644 index 00000000..1e525917 --- /dev/null +++ b/core-indexers/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/*.test.ts'], + testTimeout: 10000 +}; diff --git a/core-indexers/package-lock.json b/core-indexers/package-lock.json new file mode 100644 index 00000000..3f1f4b62 --- /dev/null +++ b/core-indexers/package-lock.json @@ -0,0 +1,6752 @@ +{ + "name": "core-indexers", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "core-indexers", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "queryapi-runner": "file:../runner" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.7.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.14.11", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "eslint": "^8.46.0", + "eslint-config-prettier": "^8.9.0", + "eslint-config-standard-with-typescript": "^37.0.0", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-n": "^16.0.1", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-promise": "^6.1.1", + "globals": "^15.8.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.3", + "ts-node": "^10.9.2", + "typescript": "^5.5.3" + } + }, + "../runner": { + "name": "queryapi-runner", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-s3": "^3.414.0", + "@google-cloud/logging-winston": "^6.0.0", + "@google-cloud/opentelemetry-cloud-trace-exporter": "^2.1.0", + "@grpc/grpc-js": "^1.9.12", + "@grpc/proto-loader": "^0.7.10", + "@near-lake/primitives": "0.5.0", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/exporter-zipkin": "^1.22.0", + "@opentelemetry/resources": "^1.22.0", + "@opentelemetry/sdk-node": "^0.50.0", + "@opentelemetry/sdk-trace-base": "^1.22.0", + "@opentelemetry/sdk-trace-node": "^1.22.0", + "@opentelemetry/semantic-conventions": "^1.22.0", + "express": "^4.18.2", + "graphql": "^16.8.1", + "long": "^5.2.3", + "node-fetch": "^2.6.11", + "node-sql-parser": "^5.0.0", + "pg": "^8.11.1", + "pg-format": "^1.0.4", + "pluralize": "^8.0.0", + "prom-client": "^14.2.0", + "redis": "^4.6.7", + "verror": "^1.10.1", + "vm2": "^3.9.19", + "winston": "^3.13.0", + "winston-transport": "^4.7.0" + }, + "devDependencies": { + "@types/express": "^4.17.17", + "@types/jest": "^29.5.3", + "@types/node": "^20.3.1", + "@types/node-fetch": "^2.6.4", + "@types/pg": "^8.10.2", + "@types/pg-format": "^1.0.2", + "@types/pluralize": "^0.0.29", + "@types/verror": "^1.10.6", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "eslint": "^8.46.0", + "eslint-config-prettier": "^8.9.0", + "eslint-config-standard-with-typescript": "^37.0.0", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-n": "^16.0.1", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-promise": "^6.1.1", + "graphql-request": "^6.1.0", + "jest": "^29.6.2", + "nodemon": "^3.1.1", + "prettier": "^3.0.0", + "testcontainers": "^10.7.2", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "typescript": "^5.1.6" + }, + "engines": { + "node": "18.17" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", + "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", + "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.9", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-module-transforms": "^7.24.9", + "@babel/helpers": "^7.24.8", + "@babel/parser": "^7.24.8", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.9", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", + "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.9", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", + "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", + "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", + "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", + "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", + "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", + "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.8", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.8", + "@babel/types": "^7.24.8", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", + "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/espree": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "dev": true, + "dependencies": { + "acorn": "^8.12.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", + "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", + "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001640", + "electron-to-chromium": "^1.4.820", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001643", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", + "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.832", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.832.tgz", + "integrity": "sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", + "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.1", + "@eslint/js": "^8.46.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.2", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-compat-utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-config-standard-with-typescript": { + "version": "37.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-37.0.0.tgz", + "integrity": "sha512-V8I/Q1eFf9tiOuFHkbksUdWO3p1crFmewecfBtRxXdnvb71BCJx+1xAknlIRZMwZioMX3/bPtMVCZsf1+AjjOw==", + "deprecated": "Please use eslint-config-love, instead.", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^5.52.0", + "eslint-config-standard": "17.1.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.52.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-n/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.6.tgz", + "integrity": "sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.8.0.tgz", + "integrity": "sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/queryapi-runner": { + "resolved": "../runner", + "link": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.2.3", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.3.tgz", + "integrity": "sha512-yCcfVdiBFngVz9/keHin9EnsrQtQtEu3nRykNy9RVp+FiPFFbPJ3Sg6Qg4+TkmH0vMP5qsTKgXSsk80HRwvdgQ==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/core-indexers/package.json b/core-indexers/package.json new file mode 100644 index 00000000..c79a6426 --- /dev/null +++ b/core-indexers/package.json @@ -0,0 +1,33 @@ +{ + "name": "core-indexers", + "version": "1.0.0", + "description": "core indexers", + "scripts": { + "test": "node --experimental-vm-modules ./node_modules/.bin/jest" + }, + "author": "", + "license": "ISC", + "dependencies": { + "queryapi-runner": "file:../runner" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "^9.7.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.14.11", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "eslint": "^8.46.0", + "eslint-config-prettier": "^8.9.0", + "eslint-config-standard-with-typescript": "^37.0.0", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-n": "^16.0.1", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-promise": "^6.1.1", + "globals": "^15.8.0", + "jest": "^29.7.0", + "ts-jest": "^29.2.3", + "ts-node": "^10.9.2", + "typescript": "^5.5.3" + } +} diff --git a/core-indexers/receiver-blocks/indexer.js b/core-indexers/receiver-blocks/indexer.js new file mode 100644 index 00000000..6f6acc50 --- /dev/null +++ b/core-indexers/receiver-blocks/indexer.js @@ -0,0 +1,324 @@ +const overallTime = Date.now(); + + // Gets index of first bit in byte array starting from startBit + function indexOfFirstBitInByteArray(bytes, startBit) { + let firstBit = startBit % 8; + for (let iByte = ~~(startBit / 8); iByte < bytes.length; iByte++) { + if (bytes[iByte] > 0) { + for (let iBit = firstBit; iBit <= 7; iBit++) { + if (bytes[iByte] & (1 << (7 - iBit))) { + return iByte * 8 + iBit; + } + } + } + firstBit = 0; + } + return -1; + } + + // Sets bit at bitIndex in uint8Array to bitValue + function setBitInBitmap(uint8Array, bitIndex, bitValue, writeZero = false) { + const newLen = (bitIndex / 8 + 1) >> 0; // Equivalent of Math.ciel using bitwise operations + let result = uint8Array; + if (uint8Array.length < newLen) { + result = new Uint8Array(new ArrayBuffer(newLen)); + result.set(uint8Array); + } + if (!bitValue && writeZero) { + result[~~(bitIndex / 8)] &= ~(1 << (7 - (bitIndex % 8))); + } else if (bitValue) { + result[~~(bitIndex / 8)] |= 1 << (7 - (bitIndex % 8)); + } + return result; + } + + // Gets bit at bitIndex in bytes + function getBitInByteArray(bytes, bitIndex) { + const byteIndex = ~~(bitIndex / 8); + const bitIndexInsideByte = bitIndex % 8; + return (bytes[byteIndex] & (1 << (7 - bitIndexInsideByte))) > 0; + } + + // takes numbers between [start, end] bits inclusive in byte array and + // returns decimal number they represent + function getNumberBetweenBits(bytes, start, end) { + let result = 0; + // Read bits from right to left + for (let bitIndex = end; bitIndex >= start; bitIndex--) { + if (getBitInByteArray(bytes, bitIndex)) { + result |= 1 << (end - bitIndex); + } + } + return result; + } + + // Writes Elias gamma coding bits for number x into result bytes array starting with index startBit. + // Returns index of the next bit after the coding. + // Examples: https://en.wikipedia.org/wiki/Elias_gamma_coding + function writeEliasGammaBits(x, result, startBit, writeZeros = false) { + if (x === 0) return { nextBit: startBit, result }; + if (x === 1) { + setBitInBitmap(result, startBit, true); + return { nextBit: startBit + 1, result }; + } + let nextBit = startBit; + const N = ~~Math.log2(x); + const remainder = x - 2 ** N; + nextBit += N; + + // Write N zeros if writeZeros is true + for (let index = startBit; writeZeros && index < nextBit; index++) { + result = setBitInBitmap(result, index, 0, writeZeros); + } + // Write 1 to separate prefix from suffix + result = setBitInBitmap(result, nextBit++, true, writeZeros); + // Write remainder in binary + for (let ri = 0; ri < N; ri++, nextBit++) { + const bitValue = (remainder & (1 << (N - 1 - ri))) > 0; + result = setBitInBitmap(result, nextBit, bitValue, writeZeros); + } + return { nextBit, result }; + } + + // stores first char (0 or 1) and then repeats alternating repeating sequences using Elias gamma coding + // pads the resulting string at the end with '0's for length to be divisible by 8 + function compressBitmapArray(uint8Array) { + let curBit = (uint8Array[0] & 0b10000000) > 0; + let curBitStretch = 0; + const resultBuffer = new ArrayBuffer(12000); + let result = new Uint8Array(resultBuffer); + let nextBit = 0; + let maxIndex = 0; + + // Write first bit to curBit + result = setBitInBitmap(result, nextBit++, curBit); + + // Write Elias gamma for alternating stretches of 1s and 0s + let lastEliasGammaStartBit = nextBit; + let ibit = 0; + for (; ibit < uint8Array.length * 8; ibit++) { + if (getBitInByteArray(uint8Array, ibit) === curBit) { + curBitStretch++; + } else { + maxIndex = curBit ? ibit - 1 : maxIndex; + lastEliasGammaStartBit = curBit ? nextBit : lastEliasGammaStartBit; + const w = writeEliasGammaBits(curBitStretch, result, nextBit); + nextBit = w.nextBit; + result = w.result; + curBit = !curBit; + curBitStretch = 1; + } + } + // Write Elias Gamma for the last stretch + maxIndex = curBit ? ibit - 1 : maxIndex; + lastEliasGammaStartBit = curBit ? nextBit : lastEliasGammaStartBit; + if (curBit) { + const w = writeEliasGammaBits(curBitStretch, result, nextBit); + nextBit = w.nextBit; + result = w.result; + } + result = result.slice(0, Math.ceil(nextBit / 8)); + + return { + result, + lastEliasGammaStartBit, + lastEliasGammaBitValue: curBit, + maxIndex, + }; + } + + // Returns first number x and corresponding coded bits length of the first occurrence of Elias gamma coding + function decodeEliasGammaEntryFromBytes(bytes, startBit = 0) { + if (!bytes || bytes.length === 0) return { x: 0, lastBit: 0 }; + const idx = indexOfFirstBitInByteArray(bytes, startBit); + if (idx < 0) { + return { x: 0, lastBit: -1 }; + } + const N = idx - startBit; + const remainder = + N === 0 ? 0 : getNumberBetweenBits(bytes, idx + 1, idx + N); + return { x: 2 ** N + remainder, lastBit: idx + N }; + } + + // Decompresses Elias-gamma coded bytes to Uint8Array + function decompressToBitmapArray(compressedBytes) { + const compressedBitLength = compressedBytes.length * 8; + let curBit = (compressedBytes[0] & 0b10000000) > 0; + const buffer = new ArrayBuffer(11000); + const result = new Uint8Array(buffer); + let compressedBitIdx = 1; + let resultBitIdx = 0; + while (compressedBitIdx < compressedBitLength) { + // Get x, the number of bits to set, and lastBit, the bit number which is the last bit of the Elias gamma coding + const { x, lastBit } = decodeEliasGammaEntryFromBytes( + compressedBytes, + compressedBitIdx + ); + compressedBitIdx = lastBit + 1; // Ensure next loop starts on next bit + for (let i = 0; curBit && i < x; i++) { + // Specifically if curBit is 1, set next x bits to 1 + setBitInBitmap(result, resultBitIdx + i, true); + } + resultBitIdx += x; + curBit = !curBit; // Switch currBit for next iteration (counting 1s, then 0s, then 1s, etc.) + if (x === 0) break; // we won't find any Elias gamma here, exiting + } + let bufferLength = Math.ceil(resultBitIdx / 8); + return result.subarray(0, bufferLength); + } + + // Adds a bit at position 'index' into the compressed bitmap by editing the compressed bitmap + // from the bit at index lastEliasGammaStartBit + function addIndexCompressedLast( + compressedBase64, + index, + lastEliasGammaStartBit, + maxIndex + ) { + const originalCompressed = Buffer.from(compressedBase64, "base64"); + const resultBuffer = Buffer.alloc(12000); + originalCompressed.copy(resultBuffer); + // let result = new Uint8Array(resultBuffer); + let result = resultBuffer; + // decode the last EG section + const { x, lastBit } = decodeEliasGammaEntryFromBytes( + originalCompressed, + lastEliasGammaStartBit + ); + let newLastEliasGammaStartBit = lastEliasGammaStartBit; + let cur; + if (index - maxIndex === 1) { + // write increased stretch of 1s + cur = writeEliasGammaBits(x + 1, result, lastEliasGammaStartBit, true); + } else if (index - maxIndex > 1) { + // write zeros + const zeros = index - maxIndex - 1; + cur = writeEliasGammaBits(zeros, result, lastBit + 1, true); + // write 1 for the `index` bit + newLastEliasGammaStartBit = cur.nextBit; + cur = writeEliasGammaBits(1, cur.result, cur.nextBit, true); + } else { + throw Error( + `addIndexCompressedLast: cannot write into ${index} before ${maxIndex}` + ); + } + // write remaining zeros + const lastEliasGammaEndBit = cur.nextBit - 1; + const remainingZeros = 8 - (lastEliasGammaEndBit % 8) - 1; + for (let i = 0; i < remainingZeros; i++) { + result = setBitInBitmap(cur.result, cur.nextBit + i, false, true); + } + + return { + compressed: result + .subarray(0, Math.ceil(cur.nextBit / 8)) + .toString("base64"), + lastEliasGammaStartBit: newLastEliasGammaStartBit, + maxIndex: index, + }; + } + + function addIndexCompressedFull(compressedBase64, index) { + const buf = Buffer.from(compressedBase64, "base64"); + const bitmap = decompressToBitmapArray(buf); + const newBitmap = setBitInBitmap(bitmap, index, true); + const { + result: compressed, + lastEliasGammaStartBit, + maxIndex, + } = compressBitmapArray(newBitmap); + return { + compressed: Buffer.from(compressed).toString("base64"), + lastEliasGammaStartBit, + maxIndex, + }; + } + + function addIndexCompressed( + compressedBase64, + index, + lastEliasGammaStartBit, + maxIndex = -1 + ) { + if (maxIndex < 0 || index <= maxIndex) { + return addIndexCompressedFull(compressedBase64, index); + } else { + return addIndexCompressedLast( + compressedBase64, + index, + lastEliasGammaStartBit, + maxIndex + ); + } + } + + const blockDate = new Date( + block.streamerMessage.block.header.timestamp / 1000000 + ) + .toISOString() + .substring(0, 10); + const actionsByReceiver = block.actions().reduce((groups, action) => { + (groups[action.receiverId] ||= []).push(action); + return groups; + }, {}); + let allReceivers = Array.from( + Object.keys(actionsByReceiver).reduce((totalReceivers, receiver) => { + totalReceivers.add(receiver); + const lastIndexOfPeriod = receiver.lastIndexOf("."); + return lastIndexOfPeriod === -1 + ? totalReceivers.add(receiver) + : totalReceivers.add(receiver.substring(lastIndexOfPeriod + 1)); + }, new Set()) + ); + const receiverMap = ( + await context.db.Receivers.upsert( + allReceivers.map((r) => ({ receiver: r })), + ["receiver"], + ["receiver"] + ) + ).reduce((map, row) => ({ ...map, [row.receiver]: row.id }), {}); + //context.log('receiverMap', JSON.stringify(receiverMap)); + const existingReceivers = + (await context.db.Bitmaps.select({ + block_date: blockDate, + receiver_id: Object.values(receiverMap), + })) ?? []; + + let startTime = Date.now(); + const upserts = Object.entries(receiverMap).map(([receiver, receiverId]) => { + const currentIndex = existingReceivers.find( + (receiver) => receiver?.receiver_id === receiverId + ); + const blockIndexInCurrentBitmap = currentIndex?.first_block_height + ? block.blockHeight - currentIndex?.first_block_height + : 0; + + const newBitmap = addIndexCompressed( + currentIndex?.bitmap ?? "", + blockIndexInCurrentBitmap, + parseInt(currentIndex?.last_elias_gamma_start_bit ?? -1), + parseInt(currentIndex?.max_index ?? -1) + ); + return { + first_block_height: currentIndex?.first_block_height ?? block.blockHeight, + block_date: blockDate, + receiver_id: receiverId, + bitmap: newBitmap.compressed, + last_elias_gamma_start_bit: newBitmap.lastEliasGammaStartBit, + max_index: newBitmap.maxIndex, + }; + }); + const processBitmapsTimer = Date.now() - startTime; + + await context.db.Bitmaps.upsert( + upserts, + ["block_date", "receiver_id"], + ["bitmap", "last_elias_gamma_start_bit", "max_index"] + ); + console.log( + `Computing bitmaps for ${ + allReceivers.length + } receivers took ${processBitmapsTimer}ms; total time ${ + Date.now() - overallTime + }ms` + ); diff --git a/core-indexers/receiver-blocks/schema.sql b/core-indexers/receiver-blocks/schema.sql new file mode 100644 index 00000000..26615cfc --- /dev/null +++ b/core-indexers/receiver-blocks/schema.sql @@ -0,0 +1,20 @@ +CREATE TABLE + "receivers" ( + "id" BIGSERIAL NOT NULL PRIMARY KEY, + "receiver" TEXT NOT NULL + ); + +CREATE UNIQUE INDEX idx_receivers_by_receiver ON receivers (receiver); + +CREATE TABLE + "bitmaps" ( + "receiver_id" bigint NOT NULL, + "block_date" date NOT NULL, + "first_block_height" int NOT NULL, + "last_elias_gamma_start_bit" int NOT NULL, + "max_index" int NOT NULL, + "bitmap" TEXT NOT NULL, + PRIMARY KEY ("block_date", "receiver_id"), + CONSTRAINT "bitmaps_receiver_id_fkey" FOREIGN KEY ("receiver_id") REFERENCES "receivers" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION + ); + diff --git a/core-indexers/receiver-blocks/unit.test.ts b/core-indexers/receiver-blocks/unit.test.ts new file mode 100644 index 00000000..1d0c103b --- /dev/null +++ b/core-indexers/receiver-blocks/unit.test.ts @@ -0,0 +1,20 @@ +import fs from 'fs'; +import LocalIndexer from 'queryapi-runner/src/local-indexer'; +import { LocalIndexerConfig } from 'queryapi-runner/src/indexer-config/indexer-config'; +import { LogLevel } from 'queryapi-runner/src/indexer-meta/log-entry'; +import path from 'path'; + +describe('Receiver Blocks Indexer Tests', () => { + const indexerConfig: LocalIndexerConfig = LocalIndexerConfig.fromObject({ + accountId: 'account.near', + functionName: 'sample_indexer', + code: fs.readFileSync(path.join(__dirname, 'indexer.js'), 'utf8'), + schema: fs.readFileSync(path.join(__dirname, 'schema.sql'), 'utf8'), + logLevel: LogLevel.INFO, + }); + + test('Try executing on a block', async () => { + const localIndexer = new LocalIndexer(indexerConfig); + await localIndexer.executeOnBlock(123621232); + }); +}); diff --git a/core-indexers/tsconfig.json b/core-indexers/tsconfig.json new file mode 100644 index 00000000..9555e472 --- /dev/null +++ b/core-indexers/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2018", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": ["es2021"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "module": "commonjs", /* Specify what module code is generated. */ + "rootDir": "..", + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + "resolveJsonModule": true, /* Enable importing .json files. */ + "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + "outDir": "dist", /* Specify an output folder for all emitted files. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/runner/.eslintrc.js b/runner/.eslintrc.js index e0c7a1ee..880c35e2 100644 --- a/runner/.eslintrc.js +++ b/runner/.eslintrc.js @@ -18,6 +18,7 @@ module.exports = { files: ['./src/**/*', './tests/**/*'], parserOptions: { project: './tsconfig.json', + tsconfigRootDir: __dirname, }, extends: [ 'standard-with-typescript', diff --git a/runner/package-lock.json b/runner/package-lock.json index 22f5932d..655a125f 100644 --- a/runner/package-lock.json +++ b/runner/package-lock.json @@ -6616,6 +6616,7 @@ "version": "37.0.0", "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-37.0.0.tgz", "integrity": "sha512-V8I/Q1eFf9tiOuFHkbksUdWO3p1crFmewecfBtRxXdnvb71BCJx+1xAknlIRZMwZioMX3/bPtMVCZsf1+AjjOw==", + "deprecated": "Please use eslint-config-love, instead.", "dev": true, "dependencies": { "@typescript-eslint/parser": "^5.52.0", diff --git a/runner/package.json b/runner/package.json index f9a5f7c9..a0df91ba 100644 --- a/runner/package.json +++ b/runner/package.json @@ -1,5 +1,5 @@ { - "name": "redis-handler", + "name": "queryapi-runner", "version": "1.0.0", "description": "", "main": "index.js", diff --git a/runner/protos/runner.proto b/runner/protos/runner.proto index 39801e65..665e64b4 100644 --- a/runner/protos/runner.proto +++ b/runner/protos/runner.proto @@ -84,4 +84,6 @@ enum ExecutionState { STOPPED = 4; // Unintentionally stopped STALLED = 5; + // Waiting to start + IDLE = 6; } diff --git a/runner/src/dml-handler/dml-handler.ts b/runner/src/dml-handler/dml-handler.ts index 7a481f3e..29feb7d3 100644 --- a/runner/src/dml-handler/dml-handler.ts +++ b/runner/src/dml-handler/dml-handler.ts @@ -11,14 +11,14 @@ export type PostgresRow = Record; export type WhereClauseMulti = Record; export type WhereClauseSingle = Record; -export interface IDmlHandler { +export interface DmlHandlerInterface { insert: (tableDefinitionNames: TableDefinitionNames, rowsToInsert: PostgresRow[]) => Promise select: (tableDefinitionNames: TableDefinitionNames, whereObject: WhereClauseMulti, limit: number | null) => Promise update: (tableDefinitionNames: TableDefinitionNames, whereObject: WhereClauseSingle, updateObject: any) => Promise upsert: (tableDefinitionNames: TableDefinitionNames, rowsToUpsert: PostgresRow[], conflictColumns: string[], updateColumns: string[]) => Promise delete: (tableDefinitionNames: TableDefinitionNames, whereObject: WhereClauseMulti) => Promise } -export default class DmlHandler implements IDmlHandler { +export default class DmlHandler implements DmlHandlerInterface { validTableNameRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/; pgClient: PgClient; tracer: Tracer; diff --git a/runner/src/dml-handler/in-memory-dml-handler.ts b/runner/src/dml-handler/in-memory-dml-handler.ts index 7a8783ef..d6fa9f1d 100644 --- a/runner/src/dml-handler/in-memory-dml-handler.ts +++ b/runner/src/dml-handler/in-memory-dml-handler.ts @@ -1,6 +1,6 @@ import { type AST, Parser } from 'node-sql-parser'; import { type TableDefinitionNames } from '../indexer'; -import { type PostgresRow, type WhereClauseMulti, type WhereClauseSingle, type IDmlHandler } from './dml-handler'; +import { type PostgresRow, type WhereClauseMulti, type WhereClauseSingle, type DmlHandlerInterface } from './dml-handler'; // TODO: Define class to represent specification interface TableSpecification { @@ -322,12 +322,18 @@ class IndexerData { } } -export default class InMemoryDmlHandler implements IDmlHandler { +export default class InMemoryDmlHandler implements DmlHandlerInterface { private readonly indexerData: IndexerData; constructor (schema: string) { const parser = new Parser(); - let schemaAST = parser.astify(schema, { database: 'Postgresql' }); + let schemaAST: AST | AST[]; + try { + schemaAST = parser.astify(schema, { database: 'Postgresql' }); + } catch (err) { + const error = err as Error; + throw new Error(`Failed to parse indexer schema: ${error.message}`); + } schemaAST = Array.isArray(schemaAST) ? schemaAST : [schemaAST]; // Ensure iterable this.indexerData = new IndexerData(schemaAST); } diff --git a/runner/src/indexer-config/indexer-config.ts b/runner/src/indexer-config/indexer-config.ts index f1279ee4..f975f484 100644 --- a/runner/src/indexer-config/indexer-config.ts +++ b/runner/src/indexer-config/indexer-config.ts @@ -49,6 +49,36 @@ export class ProvisioningConfig extends BaseConfig { } } +interface LocalIndexerConfigData { + accountId: string + functionName: string + code: string + schema: string + logLevel: LogLevel +} + +export class LocalIndexerConfig extends BaseConfig { + constructor ( + public readonly accountId: string, + public readonly functionName: string, + public readonly code: string, + public readonly schema: string, + public readonly logLevel: LogLevel + ) { + super(accountId, functionName); + } + + static fromObject (data: LocalIndexerConfigData): LocalIndexerConfig { + return new LocalIndexerConfig( + data.accountId, + data.functionName, + data.code, + data.schema, + data.logLevel + ); + } +} + interface IndexerConfigData { redisStreamKey: string accountId: string diff --git a/runner/src/indexer-meta/indexer-meta.ts b/runner/src/indexer-meta/indexer-meta.ts index 70c6fc73..f0da10b9 100644 --- a/runner/src/indexer-meta/indexer-meta.ts +++ b/runner/src/indexer-meta/indexer-meta.ts @@ -19,7 +19,13 @@ export enum MetadataFields { LAST_PROCESSED_BLOCK_HEIGHT = 'LAST_PROCESSED_BLOCK_HEIGHT' } -export default class IndexerMeta { +export interface IndexerMetaInterface { + writeLogs: (logEntries: LogEntry[]) => Promise + setStatus: (status: IndexerStatus) => Promise + updateBlockHeight: (blockHeight: number) => Promise +} + +export default class IndexerMeta implements IndexerMetaInterface { tracer = trace.getTracer('queryapi-runner-indexer-logger'); private readonly pgClient: PgClient; @@ -29,7 +35,7 @@ export default class IndexerMeta { constructor ( indexerConfig: IndexerConfig, databaseConnectionParameters: PostgresConnectionParams, - pgClientInstance: PgClient | undefined = undefined + pgClientInstance: PgClient | undefined = undefined, ) { const pgClient = pgClientInstance ?? new PgClient(databaseConnectionParameters); @@ -45,7 +51,9 @@ export default class IndexerMeta { logEntries: LogEntry[], ): Promise { const entriesArray = logEntries.filter(entry => this.shouldLog(entry.level)); - if (entriesArray.length === 0) return; + if (entriesArray.length === 0) { + return; + }; await wrapSpan(async () => { await wrapError(async () => { diff --git a/runner/src/indexer-meta/no-op-indexer-meta.ts b/runner/src/indexer-meta/no-op-indexer-meta.ts new file mode 100644 index 00000000..c5cb86ef --- /dev/null +++ b/runner/src/indexer-meta/no-op-indexer-meta.ts @@ -0,0 +1,32 @@ +import { type LocalIndexerConfig } from '../indexer-config/indexer-config'; +import { type IndexerStatus, type IndexerMetaInterface } from './indexer-meta'; +import type LogEntry from './log-entry'; +import { LogLevel } from './log-entry'; + +export default class NoOpIndexerMeta implements IndexerMetaInterface { + constructor ( + private readonly indexerConfig: LocalIndexerConfig, + ) {} + + private shouldLog (logLevel: LogLevel): boolean { + return logLevel >= this.indexerConfig.logLevel; + } + + async writeLogs (logEntries: LogEntry[]): Promise { + const entriesArray = logEntries.filter(entry => this.shouldLog(entry.level)); + if (entriesArray.length === 0) { + return; + }; + entriesArray.forEach(entry => { + console.log(`[${LogLevel[entry.level]}] [${entry.timestamp.toString()}] ${entry.message}`); + }); + } + + async setStatus (status: IndexerStatus): Promise { + console.log(`Setting status to ${status}`); + } + + async updateBlockHeight (blockHeight: number): Promise { + console.log(`Setting last processed block height to ${blockHeight}`); + } +} diff --git a/runner/src/indexer/indexer.test.ts b/runner/src/indexer/indexer.test.ts index 9de85774..63e857dc 100644 --- a/runner/src/indexer/indexer.test.ts +++ b/runner/src/indexer/indexer.test.ts @@ -7,9 +7,9 @@ import DmlHandler from '../dml-handler/dml-handler'; import type PgClient from '../pg-client'; import { LogLevel } from '../indexer-meta/log-entry'; import IndexerConfig from '../indexer-config/indexer-config'; -import IndexerMeta from '../indexer-meta'; import { IndexerStatus } from '../indexer-meta'; -import { PostgresConnectionParams } from '../pg-client'; +import type IndexerMeta from '../indexer-meta'; +import { type PostgresConnectionParams } from '../pg-client'; describe('Indexer unit tests', () => { const SIMPLE_SCHEMA = `CREATE TABLE diff --git a/runner/src/indexer/indexer.ts b/runner/src/indexer/indexer.ts index 9b827051..19f32e2a 100644 --- a/runner/src/indexer/indexer.ts +++ b/runner/src/indexer/indexer.ts @@ -6,17 +6,18 @@ import { trace, type Span } from '@opentelemetry/api'; import VError from 'verror'; import logger from '../logger'; -import type DmlHandler from '../dml-handler/dml-handler'; import LogEntry from '../indexer-meta/log-entry'; import type IndexerConfig from '../indexer-config'; -import type IndexerMeta from '../indexer-meta'; import { IndexerStatus } from '../indexer-meta'; import { wrapSpan } from '../utility'; +import { type IndexerMetaInterface } from '../indexer-meta/indexer-meta'; +import { type DmlHandlerInterface } from '../dml-handler/dml-handler'; +import assert from 'assert'; interface Dependencies { fetch?: typeof fetch - dmlHandler: DmlHandler - indexerMeta: IndexerMeta + dmlHandler: DmlHandlerInterface + indexerMeta: IndexerMetaInterface parser?: Parser }; @@ -40,11 +41,12 @@ export interface TableDefinitionNames { interface Config { hasuraAdminSecret: string hasuraEndpoint: string + } const defaultConfig: Config = { - hasuraAdminSecret: process.env.HASURA_ADMIN_SECRET, - hasuraEndpoint: process.env.HASURA_ENDPOINT, + hasuraAdminSecret: process.env.HASURA_ADMIN_SECRET = '', + hasuraEndpoint: process.env.HASURA_ENDPOINT = '', }; export default class Indexer { @@ -73,7 +75,7 @@ export default class Indexer { async execute ( block: lakePrimitives.Block, - ): Promise { + ): Promise { this.logger.debug('Executing block', { blockHeight: block.blockHeight }); const blockHeight: number = block.blockHeight; @@ -81,7 +83,6 @@ export default class Indexer { const lag = Date.now() - Math.floor(Number(block.header().timestampNanosec) / 1000000); const simultaneousPromises: Array> = []; - const allMutations: string[] = []; const logEntries: LogEntry[] = []; try { @@ -130,7 +131,6 @@ export default class Indexer { } this.IS_FIRST_EXECUTION = false; } - return allMutations; } buildContext (blockHeight: number, logEntries: LogEntry[]): Context { @@ -259,7 +259,7 @@ export default class Indexer { const tableNameToDefinitionNamesMapping = this.getTableNameToDefinitionNamesMapping(this.indexerConfig.schema); const tableNames = Array.from(tableNameToDefinitionNamesMapping.keys()); const sanitizedTableNames = new Set(); - const dmlHandler: DmlHandler = this.deps.dmlHandler; + const dmlHandler: DmlHandlerInterface = this.deps.dmlHandler; // Generate and collect methods for each table name const result = tableNames.reduce((prev, tableName) => { @@ -333,6 +333,7 @@ export default class Indexer { } async runGraphQLQuery (operation: string, variables: any, blockHeight: number, hasuraRoleName: string | null, logError: boolean = true): Promise { + assert(this.config.hasuraAdminSecret !== '' && this.config.hasuraEndpoint !== '', 'hasuraAdminSecret and hasuraEndpoint env variables are required'); const response: Response = await this.deps.fetch(`${this.config.hasuraEndpoint}/v1/graphql`, { method: 'POST', headers: { diff --git a/runner/src/lake-client/lake-client.test.ts b/runner/src/lake-client/lake-client.test.ts index de28553c..fbae9ca6 100644 --- a/runner/src/lake-client/lake-client.test.ts +++ b/runner/src/lake-client/lake-client.test.ts @@ -1,18 +1,7 @@ import { GetObjectCommand, type S3Client } from '@aws-sdk/client-s3'; import LakeClient from './lake-client'; -import type RedisClient from '../redis-client'; describe('LakeClient', () => { - let transparentRedis = { - getStreamerMessage: jest.fn() - } as unknown as RedisClient; - - beforeEach(() => { - transparentRedis = { - getStreamerMessage: jest.fn() - } as unknown as RedisClient; - }); - test('Indexer.fetchBlock() should fetch the block and shards from S3 upon cache miss', async () => { const blockHeight = 85233529; const blockHash = 'xyz'; @@ -36,81 +25,7 @@ describe('LakeClient', () => { const mockS3 = { send: mockSend, } as unknown as S3Client; - const client = new LakeClient('mainnet', mockS3, transparentRedis); - - const block = await client.fetchBlock(blockHeight); - - expect(mockSend).toHaveBeenCalledTimes(5); - expect(JSON.stringify(mockSend.mock.calls[0][0])).toStrictEqual(JSON.stringify(new GetObjectCommand({ - Bucket: 'near-lake-data-mainnet', - Key: `${blockHeight.toString().padStart(12, '0')}/block.json` - }))); - expect(JSON.stringify(mockSend.mock.calls[1][0])).toStrictEqual(JSON.stringify(new GetObjectCommand({ - Bucket: 'near-lake-data-mainnet', - Key: `${blockHeight.toString().padStart(12, '0')}/shard_0.json` - }))); - - expect(block.blockHeight).toEqual(blockHeight); - expect(block.blockHash).toEqual(blockHash); - }); - - test('fetchBlock should fetch the streamer message from cache, convert it to block, and return it', async () => { - const blockHeight = 85233529; - const blockHash = 'xyz'; - const getMessage = jest.fn() - .mockReturnValueOnce(JSON.stringify( - { - block: { - chunks: [0], - header: { - height: blockHeight, - hash: blockHash, - } - }, - shards: {} - } - )); - const mockRedis = { - getStreamerMessage: getMessage - } as unknown as RedisClient; - const mockS3 = {} as unknown as S3Client; - const client = new LakeClient('mainnet', mockS3, mockRedis); - - const block = await client.fetchBlock(blockHeight); - - expect(getMessage).toHaveBeenCalledTimes(1); - expect(JSON.stringify(getMessage.mock.calls[0])).toEqual( - `[${blockHeight}]` - ); - - expect(block.blockHeight).toEqual(blockHeight); - expect(block.blockHash).toEqual(blockHash); - }); - - test('fetchBlock should fetch the block and shards from S3 upon cache miss', async () => { - const blockHeight = 85233529; - const blockHash = 'xyz'; - const mockSend = jest.fn() - .mockReturnValueOnce({ // block - Body: { - transformToString: () => JSON.stringify({ - chunks: [0, 1, 2, 3], - header: { - height: blockHeight, - hash: blockHash, - } - }) - } - }) - .mockReturnValue({ // shard - Body: { - transformToString: () => JSON.stringify({}) - } - }); - const mockS3 = { - send: mockSend, - } as unknown as S3Client; - const client = new LakeClient('mainnet', mockS3, transparentRedis); + const client = new LakeClient('mainnet', mockS3); const block = await client.fetchBlock(blockHeight); @@ -123,7 +38,6 @@ describe('LakeClient', () => { Bucket: 'near-lake-data-mainnet', Key: `${blockHeight.toString().padStart(12, '0')}/shard_0.json` }))); - expect(transparentRedis.getStreamerMessage).toHaveBeenCalledTimes(1); expect(block.blockHeight).toEqual(blockHeight); expect(block.blockHash).toEqual(blockHash); diff --git a/runner/src/lake-client/lake-client.ts b/runner/src/lake-client/lake-client.ts index a8541e19..3273f31b 100644 --- a/runner/src/lake-client/lake-client.ts +++ b/runner/src/lake-client/lake-client.ts @@ -1,7 +1,5 @@ import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; import { Block } from '@near-lake/primitives'; -import { METRICS } from '../metrics'; -import RedisClient from '../redis-client'; export default class LakeClient { constructor ( @@ -9,8 +7,7 @@ export default class LakeClient { private readonly s3Client: S3Client = new S3Client({ maxAttempts: 5, retryMode: 'adaptive' - }), - private readonly redisClient: RedisClient = new RedisClient() + }) ) {} // pad with 0s to 12 digits @@ -68,15 +65,6 @@ export default class LakeClient { } async fetchBlock (blockHeight: number): Promise { - const cachedMessage = await this.redisClient.getStreamerMessage(blockHeight); - if (cachedMessage) { - METRICS.CACHE_HIT.inc(); - const parsedMessage = JSON.parse(cachedMessage); - return Block.fromStreamerMessage(parsedMessage); - } else { - METRICS.CACHE_MISS.inc(); - } - const block = await this.fetchBlockPromise(blockHeight); const shards = await Promise.all(this.fetchShards(blockHeight, block.chunks.length)); diff --git a/runner/src/local-indexer/index.ts b/runner/src/local-indexer/index.ts new file mode 100644 index 00000000..b0546169 --- /dev/null +++ b/runner/src/local-indexer/index.ts @@ -0,0 +1 @@ +export { default } from './local-indexer'; diff --git a/runner/src/local-indexer/local-indexer.ts b/runner/src/local-indexer/local-indexer.ts new file mode 100644 index 00000000..d84dfeba --- /dev/null +++ b/runner/src/local-indexer/local-indexer.ts @@ -0,0 +1,33 @@ +import InMemoryDmlHandler from '../dml-handler/in-memory-dml-handler'; +import IndexerConfig from '../indexer-config'; +import { type LocalIndexerConfig } from '../indexer-config/indexer-config'; +import NoOpIndexerMeta from '../indexer-meta/no-op-indexer-meta'; +import Indexer from '../indexer/indexer'; +import LakeClient from '../lake-client/lake-client'; + +export default class LocalIndexer { + public readonly indexer: Indexer; + private readonly lakeClient: LakeClient; + + constructor (config: LocalIndexerConfig) { + const fullIndexerConfig: IndexerConfig = IndexerConfig.fromObject({ + redisStreamKey: 'local-indexer', + accountId: config.accountId, + functionName: config.functionName, + version: 0, + code: config.code, + schema: config.schema, + logLevel: config.logLevel, + }); + const dmlHandler = new InMemoryDmlHandler(config.schema); + const indexerMeta = new NoOpIndexerMeta(config); + this.indexer = new Indexer(fullIndexerConfig, { indexerMeta, dmlHandler }); + this.lakeClient = new LakeClient(); + } + + async executeOnBlock (blockHeight: number): Promise { + // TODO: Cache Block data locally + const block = await this.lakeClient.fetchBlock(blockHeight); + await this.indexer.execute(block); + } +} diff --git a/runner/src/stream-handler/worker.ts b/runner/src/stream-handler/worker.ts index 34b78351..1403d660 100644 --- a/runner/src/stream-handler/worker.ts +++ b/runner/src/stream-handler/worker.ts @@ -1,7 +1,7 @@ import { isMainThread, parentPort, workerData } from 'worker_threads'; import { trace, type Span, context } from '@opentelemetry/api'; import promClient from 'prom-client'; -import type { Block } from '@near-lake/primitives'; +import { Block } from '@near-lake/primitives'; import Indexer from '../indexer'; import RedisClient from '../redis-client'; @@ -200,6 +200,17 @@ async function blockQueueConsumer (workerContext: WorkerContext): Promise } async function generateQueuePromise (workerContext: WorkerContext, blockHeight: number, streamMessageId: string): Promise { + const cachedMessage = await workerContext.redisClient.getStreamerMessage(blockHeight); + if (cachedMessage) { + METRICS.CACHE_HIT.inc(); + const parsedMessage = JSON.parse(cachedMessage); + return { + block: Block.fromStreamerMessage(parsedMessage), + streamMessageId + }; + } else { + METRICS.CACHE_MISS.inc(); + } const block = await workerContext.lakeClient.fetchBlock(blockHeight).catch((err) => { workerContext.logger.error(`Error fetching block ${blockHeight}`, err); return undefined; From b8c7f3f37a73f6ab19d157214674388aab06b186 Mon Sep 17 00:00:00 2001 From: Darun Seethammagari Date: Wed, 24 Jul 2024 23:18:51 +0530 Subject: [PATCH 08/10] fix: Implement retry on tracking foreign keys and resolve hasura env issue (#919) This PR adds exponential retry for the tracking of foreign keys. For now I'm keeping the implementation of expo retry in provisioner light for two reasons: 1. I want to confirm that expo retry actually solves the issue. 2. I plan to refactor the provisioning process entirely. I also noticed a bug where the env variables were being set to empty in all cases instead of only if null. I fix this bug as well. --- runner/src/indexer/indexer.ts | 4 +-- runner/src/provisioner/provisioner.test.ts | 8 ++++- runner/src/provisioner/provisioner.ts | 35 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/runner/src/indexer/indexer.ts b/runner/src/indexer/indexer.ts index 19f32e2a..3e1891b1 100644 --- a/runner/src/indexer/indexer.ts +++ b/runner/src/indexer/indexer.ts @@ -45,8 +45,8 @@ interface Config { } const defaultConfig: Config = { - hasuraAdminSecret: process.env.HASURA_ADMIN_SECRET = '', - hasuraEndpoint: process.env.HASURA_ENDPOINT = '', + hasuraAdminSecret: process.env.HASURA_ADMIN_SECRET ?? '', + hasuraEndpoint: process.env.HASURA_ENDPOINT ?? '', }; export default class Indexer { diff --git a/runner/src/provisioner/provisioner.test.ts b/runner/src/provisioner/provisioner.test.ts index 90d93218..8ef12371 100644 --- a/runner/src/provisioner/provisioner.test.ts +++ b/runner/src/provisioner/provisioner.test.ts @@ -17,6 +17,10 @@ describe('Provisioner', () => { const functionName = 'test-function'; const databaseSchema = 'CREATE TABLE blocks (height numeric)'; indexerConfig = new IndexerConfig('', accountId, functionName, 0, '', databaseSchema, LogLevel.INFO); + const testingRetryConfig = { + maxRetries: 5, + baseDelay: 10 + }; const setProvisioningStatusQuery = `INSERT INTO ${indexerConfig.schemaName()}.sys_metadata (attribute, value) VALUES ('STATUS', 'PROVISIONING') ON CONFLICT (attribute) DO UPDATE SET value = EXCLUDED.value RETURNING *`; const logsDDL = expect.any(String); const metadataDDL = expect.any(String); @@ -68,7 +72,7 @@ describe('Provisioner', () => { }; }); - provisioner = new Provisioner(hasuraClient, adminPgClient, cronPgClient, undefined, crypto, pgFormat, PgClient as any); + provisioner = new Provisioner(hasuraClient, adminPgClient, cronPgClient, undefined, crypto, pgFormat, PgClient as any, testingRetryConfig); indexerConfig = new IndexerConfig('', accountId, functionName, 0, '', databaseSchema, LogLevel.INFO); }); @@ -318,12 +322,14 @@ describe('Provisioner', () => { hasuraClient.trackForeignKeyRelationships = jest.fn().mockRejectedValue(error); await expect(provisioner.provisionUserApi(indexerConfig)).rejects.toThrow('Failed to provision endpoint: Failed to track foreign key relationships: some error'); + expect(hasuraClient.trackForeignKeyRelationships).toHaveBeenCalledTimes(testingRetryConfig.maxRetries); }); it('throws an error when it fails to add permissions to tables', async () => { hasuraClient.addPermissionsToTables = jest.fn().mockRejectedValue(error); await expect(provisioner.provisionUserApi(indexerConfig)).rejects.toThrow('Failed to provision endpoint: Failed to add permissions to tables: some error'); + expect(hasuraClient.addPermissionsToTables).toHaveBeenCalledTimes(testingRetryConfig.maxRetries); }); it('throws when grant cron access fails', async () => { diff --git a/runner/src/provisioner/provisioner.ts b/runner/src/provisioner/provisioner.ts index 5533c677..f9d4e511 100644 --- a/runner/src/provisioner/provisioner.ts +++ b/runner/src/provisioner/provisioner.ts @@ -39,6 +39,11 @@ interface Config { postgresPort: number } +interface RetryConfig { + maxRetries: number + baseDelay: number +} + const defaultConfig: Config = { cronDatabase: process.env.CRON_DATABASE, pgBouncerHost: process.env.PGHOST_PGBOUNCER ?? process.env.PGHOST, @@ -47,6 +52,11 @@ const defaultConfig: Config = { postgresPort: Number(process.env.PGPORT) }; +const defaultRetryConfig: RetryConfig = { + maxRetries: 5, + baseDelay: 1000 +}; + export default class Provisioner { tracer: Tracer = trace.getTracer('queryapi-runner-provisioner'); @@ -57,7 +67,8 @@ export default class Provisioner { private readonly config: Config = defaultConfig, private readonly crypto: typeof cryptoModule = cryptoModule, private readonly pgFormat: typeof pgFormatLib = pgFormatLib, - private readonly PgClient: typeof PgClientClass = PgClientClass + private readonly PgClient: typeof PgClientClass = PgClientClass, + private readonly retryConfig: RetryConfig = defaultRetryConfig, ) {} generatePassword (length: number = DEFAULT_PASSWORD_LENGTH): string { @@ -336,15 +347,33 @@ export default class Provisioner { await this.trackTables(schemaName, updatedTableNames, databaseName); - await this.trackForeignKeyRelationships(schemaName, databaseName); + await this.exponentialRetry(async () => { + await this.trackForeignKeyRelationships(schemaName, databaseName); + }); - await this.addPermissionsToTables(indexerConfig, updatedTableNames, ['select', 'insert', 'update', 'delete']); + await this.exponentialRetry(async () => { + await this.addPermissionsToTables(indexerConfig, updatedTableNames, ['select', 'insert', 'update', 'delete']); + }); }, 'Failed to provision endpoint' ); }, this.tracer, 'provision indexer resources'); } + async exponentialRetry (fn: () => Promise): Promise { + let lastError = null; + for (let i = 0; i < this.retryConfig.maxRetries; i++) { + try { + await fn(); + return; + } catch (e) { + lastError = e; + await new Promise((resolve) => setTimeout(resolve, this.retryConfig.baseDelay * (2 ** i))); + } + } + throw lastError; + } + async getPostgresConnectionParameters (userName: string): Promise { const userDbConnectionParameters: HasuraDatabaseConnectionParameters = await this.hasuraClient.getDbConnectionParameters(userName); return { From 5dd8637ef2dda808857f84b93549d6ececeeb275 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <42101107+Kevin101Zhang@users.noreply.github.com> Date: Wed, 24 Jul 2024 15:00:24 -0400 Subject: [PATCH 09/10] fix: Add gensonjs to changed selectMethod and selectEvent to proper TS shape (#918) --- frontend/package-lock.json | 7 + frontend/package.json | 1 + .../Editor/EditorComponents/GenerateCode.jsx | 72 ------- .../Editor/EditorComponents/GenerateCode.tsx | 164 +++++++++++++++ frontend/src/pages/api/generateCode.ts | 94 ++++++--- frontend/src/test/api/generateCode.test.js | 189 ------------------ frontend/src/test/api/generateCode.test.ts | 140 +++++++++++++ 7 files changed, 383 insertions(+), 284 deletions(-) delete mode 100644 frontend/src/components/Editor/EditorComponents/GenerateCode.jsx create mode 100644 frontend/src/components/Editor/EditorComponents/GenerateCode.tsx delete mode 100644 frontend/src/test/api/generateCode.test.js create mode 100644 frontend/src/test/api/generateCode.test.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d3f4c449..72ea96a9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,6 +15,7 @@ "@near-lake/primitives": "0.5.0", "bootstrap": "^5.2.3", "buffer": "^6.0.3", + "genson-js": "^0.0.8", "graphiql": "3.0.6", "graphql": "^16.8.1", "graphql-ws": "^5.16.0", @@ -7146,6 +7147,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/genson-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/genson-js/-/genson-js-0.0.8.tgz", + "integrity": "sha512-4NUusDTwF+lzYh72uKV+Uvpky9iPO+YDIMpGImA5pbHfLV9HwgRCA4hYjGu78V4J4Cx2IZRTFfRERn9aUs74mw==", + "license": "Apache-2.0" + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 98e5c4e1..c2480305 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,6 +26,7 @@ "@near-lake/primitives": "0.5.0", "bootstrap": "^5.2.3", "buffer": "^6.0.3", + "genson-js": "^0.0.8", "graphiql": "3.0.6", "graphql": "^16.8.1", "graphql-ws": "^5.16.0", diff --git a/frontend/src/components/Editor/EditorComponents/GenerateCode.jsx b/frontend/src/components/Editor/EditorComponents/GenerateCode.jsx deleted file mode 100644 index dedc6f63..00000000 --- a/frontend/src/components/Editor/EditorComponents/GenerateCode.jsx +++ /dev/null @@ -1,72 +0,0 @@ -//Dummy Component to generate code accessable accountId=test and indexerName=test and -import { useState } from 'react'; - -const GenerateCode = () => { - const [contractFilter, setContractFilter] = useState(''); - const [selectedMethods, setSelectedMethods] = useState([]); - const [selectedEvents, setSelectedEvents] = useState([]); - const [generatedCode, setGeneratedCode] = useState({ jsCode: '', sqlCode: '' }); - - const handleGenerateCode = async () => { - const response = await fetch('/api/generateCode', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ contractFilter, selectedMethods, selectedEvents }), - }); - const data = await response.json(); - setGeneratedCode(data); - }; - - return ( -
-
-

Generate Code

-
- setContractFilter(e.target.value)} - className="w-full p-3 border border-gray-300 rounded-md" - /> -
-
- setSelectedMethods(e.target.value.split(','))} - className="w-full p-3 border border-gray-300 rounded-md" - /> -
-
- setSelectedEvents(e.target.value.split(','))} - className="w-full p-3 border border-gray-300 rounded-md" - /> -
- -
-

Generated JavaScript Code

-
{generatedCode.jsCode}
-
-
-

Generated SQL Code

-
{generatedCode.sqlCode}
-
-
-
- ); -}; - -export default GenerateCode; diff --git a/frontend/src/components/Editor/EditorComponents/GenerateCode.tsx b/frontend/src/components/Editor/EditorComponents/GenerateCode.tsx new file mode 100644 index 00000000..bf41fedb --- /dev/null +++ b/frontend/src/components/Editor/EditorComponents/GenerateCode.tsx @@ -0,0 +1,164 @@ +//Dummy component +import { useState } from 'react'; + +type Schema = { + type: string; + properties?: Record; + required?: string[]; +}; + +type Method = { + method_name: string; + schema: Schema; +}; + +type Event = { + event_name: string; + schema: Schema; +}; + +const GenerateCode = () => { + const [contractFilter, setContractFilter] = useState(''); + const [selectedMethods, setSelectedMethods] = useState([]); + const [selectedEvents, setSelectedEvents] = useState([]); + const [generatedCode, setGeneratedCode] = useState<{ jsCode: string; sqlCode: string }>({ jsCode: '', sqlCode: '' }); + + const handleGenerateCode = async () => { + const response = await fetch('/api/generateCode', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ contractFilter, selectedMethods, selectedEvents }), + }); + const data = await response.json(); + setGeneratedCode(data); + }; + + const handleMethodChange = (index: number, field: keyof Method, value: string) => { + const updatedMethods = [...selectedMethods]; + if (field === 'schema') { + try { + updatedMethods[index] = { ...updatedMethods[index], schema: JSON.parse(value) }; + } catch (e) { + console.error('Invalid JSON format'); + } + } else { + updatedMethods[index] = { ...updatedMethods[index], [field]: value }; + } + setSelectedMethods(updatedMethods); + }; + + const handleEventChange = (index: number, field: keyof Event, value: string) => { + const updatedEvents = [...selectedEvents]; + if (field === 'schema') { + try { + updatedEvents[index] = { ...updatedEvents[index], schema: JSON.parse(value) }; + } catch (e) { + console.error('Invalid JSON format'); + } + } else { + updatedEvents[index] = { ...updatedEvents[index], [field]: value }; + } + setSelectedEvents(updatedEvents); + }; + + const addMethod = () => { + setSelectedMethods([...selectedMethods, { method_name: '', schema: { type: 'object' } }]); + }; + + const addEvent = () => { + setSelectedEvents([...selectedEvents, { event_name: '', schema: { type: 'object' } }]); + }; + + return ( +
+
+

Generate Code

+ +
+ setContractFilter(e.target.value)} + className="w-full p-3 border border-gray-300 rounded-md" + /> +
+ +
+

Selected Methods

+ {selectedMethods.map((method, index) => ( +
+ handleMethodChange(index, 'method_name', e.target.value)} + className="w-full p-3 border border-gray-300 rounded-md mb-2" + /> +