diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e17bd5d8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,5 @@ +[submodule ".scripts"] + path = .scripts + url = git@github.com:Congress-Dev/scripts.git + branch = main + diff --git a/.scripts b/.scripts new file mode 160000 index 00000000..3bbf6fdc --- /dev/null +++ b/.scripts @@ -0,0 +1 @@ +Subproject commit 3bbf6fdc7d84bbf985c58cb64bcacd263d0a29e3 diff --git a/backend/billparser/actions/__init__.py b/backend/billparser/actions/__init__.py index de3fb984..0f800741 100644 --- a/backend/billparser/actions/__init__.py +++ b/backend/billparser/actions/__init__.py @@ -29,6 +29,7 @@ class ActionType(str, Enum): REDESIGNATE = "REDESIGNATE" REPEAL = "REPEAL" EFFECTIVE_DATE = "EFFECTIVE-DATE" + SUNSET = "SUNSET" TABLE_OF_CONTENTS = "TABLE-OF-CONTENTS" TABLE_OF_CHAPTERS = "TABLE-OF-CHAPTERS" INSERT_CHAPTER_AT_END = "INSERT-CHAPTER-AT-END" @@ -38,6 +39,7 @@ class ActionType(str, Enum): DATE = "DATE" FINANCIAL = "FINANCIAL" TRANSFER_FUNDS = "TRANSFER-FUNDS" + RECISSION = "RECISSION" # TODO: This whole file is some honkin bullshit. It's entirely unsustainable, but at the same time, unless I can get them to follow standards, I'm not sure @@ -134,6 +136,7 @@ class ActionType(str, Enum): ], ActionType.EFFECTIVE_DATE: [ r"The amendments made by this section shall apply to taxable years beginning after (?P.+?)\.", + r"The amendments made by (?P.+?) shall take effect on (?P.+?), and", r"not later than (?P\d+) (?P(hour|day|week|month|year)s?) after the (?:date of )?(?:the )?enactment of (?:(this|the .*?)) Act", r"not later than (?P\d+) (?P(hour|day|week|month|year)s?) after the (?:date of )?(?:the )?enactment of (?:(this|the .*?)) Act", r"Beginning on the date that is (?P\d+) (?P(hour|day|week|month|year)s?) after the (?:date of )(?:the )?enactment of this Act", @@ -146,6 +149,12 @@ class ActionType(str, Enum): r"(?P\d+) (?P(hour|day|week|month|year)s?) after the effective date of this Act.", r"This Act shall take effect (?Pone) (?P(hour|day|week|month|year)s?) after the date of enactment.", ], + ActionType.SUNSET: [ + r"(?P.+?) shall (?:cease to have effect|cease to be effective|expire|terminate) on (?P.+?)\.", + r"(?P.+?) shall (?:cease to have effect|cease to be effective|expire|terminate) (?P\d+) (?P(hour|day|week|month|year)s?) after (?P.+?)\.", + r"The (?P.+?) shall (?:cease to have effect|cease to be effective|expire|terminate) on (?P.+?)\.", + r"The authority (?:provided by|under) (?P.+?) shall (?:cease to have effect|cease to be effective|expire|terminate) on (?P.+?)\.", + ], ActionType.TABLE_OF_CONTENTS: [ r"The table of contents (for|of) this Act is as follows:" ], @@ -174,6 +183,11 @@ class ActionType(str, Enum): r"Notwithstanding any other provision of law, amounts made available to carry out (?P.*) shall be made available to (?P.*) to carry out (?P.*)", r"There is appropriated to the (?P.*?), out of any money in the (?P(Treasury)) not otherwise appropriated, (?P\$[\d,]*) for (?:the )?fiscal year (?P\d{4}), to remain available (?P.*)\.", ], + ActionType.RECISSION: [ + r"The (?Punobligated balances of amounts appropriated by (?Psection .+? of Public Law .+?)) (?:\((?P.+?)\) )?are rescinded\.", + r"(?PThe unobligated balances of amounts appropriated by (?Psection .+? of Public Law .+?)) (?:\((?P.+?)\) )?are rescinded\.", + r"(?P.+?) (?:appropriated by (?Psection .+? of Public Law .+?)) (?:\((?P.+?)\) )?are rescinded\.", + ], } SuchCodeRegex = re.compile(r"(Section|paragraph) (?P
\d*)\(", re.IGNORECASE) @@ -199,6 +213,8 @@ class Action(TypedDict): to_remove_section: Optional[str] redesignation: Optional[str] effective_date: Optional[str] + sunset_date: Optional[str] + trigger_date: Optional[str] document_title: Optional[str] term: Optional[str] term_def: Optional[str] @@ -212,6 +228,8 @@ class Action(TypedDict): fiscal_year: Optional[str] available: Optional[str] section: Optional[str] + section_ref: Optional[str] + stat_ref: Optional[str] def determine_action(text: str) -> Dict[ActionType, Action]: @@ -251,6 +269,13 @@ def determine_action(text: str) -> Dict[ActionType, Action]: if action == ActionType.INSERT_TEXT_BEFORE: if gg.get("period_at_end", None) is None: gg["period_at_end"] = True + if action == ActionType.SUNSET: + if gg.get("amount", None) is not None: + try: + gg["amount"] = str(int(gg["amount"])) + except: + if gg["amount"] == "one": + gg["amount"] = "1" gg["REGEX"] = c gg["action_type"] = action actions[action] = gg diff --git a/backend/billparser/actions/parser.py b/backend/billparser/actions/parser.py index 269846ca..33b92041 100644 --- a/backend/billparser/actions/parser.py +++ b/backend/billparser/actions/parser.py @@ -376,6 +376,9 @@ def insert_subsection_end( # We assume our target citation is the parent section, so to insert at the end we need to find the last child query = select(USCContent).where(USCContent.usc_ident == citation) results = session.execute(query).all() + if len(results) == 0: + logging.warning("No target section found", extra={"usc_ident": citation}) + return [] target_section = results[0][0] query = ( select(USCContent) @@ -668,6 +671,7 @@ def recursively_extract_actions( citations=cite_list, ) PARSER_SESSION.add(new_action) + PARSER_SESSION.flush() apply_action( content_by_parent_id, new_action, parent_actions, version_id ) diff --git a/backend/billparser/importers/cleanup.py b/backend/billparser/importers/cleanup.py index 6235ef7a..f55fe8e0 100644 --- a/backend/billparser/importers/cleanup.py +++ b/backend/billparser/importers/cleanup.py @@ -87,5 +87,5 @@ def cleanup_legislation(): if __name__ == "__main__": session = Session() - cleanup_legislation() + # cleanup_legislation() cleanup_usc_release(session) diff --git a/backend/billparser/prompt_runners/utils.py b/backend/billparser/prompt_runners/utils.py index cc0ae4a2..a82a77a1 100644 --- a/backend/billparser/prompt_runners/utils.py +++ b/backend/billparser/prompt_runners/utils.py @@ -17,8 +17,9 @@ def run_query( query: str, model: str = "ollama/qwen2.5:32b", *, - num_ctx: int = 2048, + num_ctx: int = 4096, json: bool = True, + max_tokens: int = 10000, ) -> dict: start_time = time.time() response = completion( @@ -27,7 +28,7 @@ def run_query( api_base=llm_host, format="json" if json else None, timeout=60, - max_tokens=10000, + max_tokens=max_tokens, num_ctx=num_ctx, ) end_time = time.time() diff --git a/backend/billparser/tests/test_actions.py b/backend/billparser/tests/test_actions.py index a3dbc918..5de96251 100644 --- a/backend/billparser/tests/test_actions.py +++ b/backend/billparser/tests/test_actions.py @@ -177,6 +177,16 @@ def test_one_day(self): self.assertEqual(result["amount"], "1") self.assertEqual(result["unit"], "day") + def test_amendments_take_effect_specific_date(self): + text = """The amendments made by paragraph (1) shall take effect on July 1, 2026, and shall apply with respect to award year 2026-2027 and each subsequent award year, as determined under the Higher Education Act of 1965.""" + result = determine_action(text) + self.assertIn(ActionType.EFFECTIVE_DATE, result) + result = result[ActionType.EFFECTIVE_DATE] + self.assertEqual(result["target"], "paragraph (1)") + self.assertEqual(result["effective_date"], "July 1, 2026") + self.assertEqual(result["amount"], "0") + self.assertEqual(result["unit"], "days") + class TestTermDefinitionRef(TestCase): def test_in_popular_name(self): @@ -194,3 +204,87 @@ def test_regular(self): self.assertIn(ActionType.TERM_DEFINITION_SECTION, result) result = result[ActionType.TERM_DEFINITION_SECTION] self.assertEqual(result["term"], "covered device") + + +class TestRecission(TestCase): + def test_recission_with_stat_ref(self): + text = """The unobligated balances of amounts appropriated by section 21001(a) of Public Law 117-169 (136 Stat. 2015) are rescinded.""" + result = determine_action(text) + self.assertIn(ActionType.RECISSION, result) + result = result[ActionType.RECISSION] + self.assertEqual(result["section_ref"], "section 21001(a) of Public Law 117-169") + self.assertEqual(result["stat_ref"], "136 Stat. 2015") + self.assertIn("unobligated balances", result["target"]) + + def test_recission_without_stat_ref(self): + text = """The unobligated balances of amounts appropriated by section 5001 of Public Law 118-42 are rescinded.""" + result = determine_action(text) + self.assertIn(ActionType.RECISSION, result) + result = result[ActionType.RECISSION] + self.assertEqual(result["section_ref"], "section 5001 of Public Law 118-42") + self.assertIsNone(result.get("stat_ref")) + self.assertIn("unobligated balances", result["target"]) + + +class TestSunset(TestCase): + def test_sunset_cease_to_have_effect_on_date(self): + text = """This Act shall cease to have effect on December 31, 2025.""" + result = determine_action(text) + self.assertIn(ActionType.SUNSET, result) + result = result[ActionType.SUNSET] + self.assertEqual(result["target"], "This Act") + self.assertEqual(result["sunset_date"], "December 31, 2025") + + def test_sunset_expire_on_date(self): + text = """The program shall expire on September 30, 2026.""" + result = determine_action(text) + self.assertIn(ActionType.SUNSET, result) + result = result[ActionType.SUNSET] + self.assertEqual(result["target"], "The program") + self.assertEqual(result["sunset_date"], "September 30, 2026") + + def test_sunset_terminate_on_date(self): + text = """The temporary authority shall terminate on January 1, 2027.""" + result = determine_action(text) + self.assertIn(ActionType.SUNSET, result) + result = result[ActionType.SUNSET] + self.assertEqual(result["target"], "The temporary authority") + self.assertEqual(result["sunset_date"], "January 1, 2027") + + def test_sunset_with_time_period_after_trigger(self): + text = """The pilot program shall cease to be effective 3 years after the date of enactment of this Act.""" + result = determine_action(text) + self.assertIn(ActionType.SUNSET, result) + result = result[ActionType.SUNSET] + self.assertEqual(result["target"], "The pilot program") + self.assertEqual(result["amount"], "3") + self.assertEqual(result["unit"], "years") + self.assertEqual(result["trigger_date"], "the date of enactment of this Act") + + @skip("Not implemented") + def test_sunset_authority_provided_by(self): + text = """The authority provided by this section shall expire on December 31, 2028.""" + result = determine_action(text) + self.assertIn(ActionType.SUNSET, result) + result = result[ActionType.SUNSET] + self.assertEqual(result["target"], "this section") + self.assertEqual(result["sunset_date"], "December 31, 2028") + + @skip("Not implemented") + def test_sunset_authority_under(self): + text = """The authority under section 123 shall cease to have effect on June 30, 2025.""" + result = determine_action(text) + self.assertIn(ActionType.SUNSET, result) + result = result[ActionType.SUNSET] + self.assertEqual(result["target"], "section 123") + self.assertEqual(result["sunset_date"], "June 30, 2025") + + def test_sunset_with_days_after_trigger(self): + text = """The emergency powers shall terminate 90 days after the President declares the emergency over.""" + result = determine_action(text) + self.assertIn(ActionType.SUNSET, result) + result = result[ActionType.SUNSET] + self.assertEqual(result["target"], "The emergency powers") + self.assertEqual(result["amount"], "90") + self.assertEqual(result["unit"], "days") + self.assertEqual(result["trigger_date"], "the President declares the emergency over") diff --git a/frontend/package.json b/frontend/package.json index fc71c6cf..9d929c75 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,9 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "@types/node": "^16.0.0", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", "buffer": "^6.0.3", "crypto": "^1.0.1", "crypto-browserify": "^3.12.0", @@ -38,6 +41,7 @@ "react-scripts": "5.0.1", "sass": "^1.69.5", "stream-browserify": "^3.0.0", + "typescript": "^5.0.0", "web-vitals": "^2.1.4", "xmldoc": "^1.1.2" }, diff --git a/frontend/src/common/api.js b/frontend/src/common/api.js index 3e60dee2..861bb8b7 100644 --- a/frontend/src/common/api.js +++ b/frontend/src/common/api.js @@ -404,12 +404,13 @@ export const getBillActionsv2 = (legislationVersionId) => { .then(handleStatus) .catch(toastError); }; + export const getBillVersionDiffSummaryv2 = (legislationVersionId) => { - return fetch( - `${endPv2}/legislation_version/${legislationVersionId}/diffs`, - ) - .then(handleStatus) - .catch(toastError); + return fetch( + `${endPv2}/legislation_version/${legislationVersionId}/diffs`, + ) + .then(handleStatus) + .catch(toastError); }; export const getBillVersionDiffForSection = ( session, diff --git a/frontend/src/components/appropriation-item/index.jsx b/frontend/src/components/appropriation-item/index.jsx index 12b9d2c7..0b5e3aca 100644 --- a/frontend/src/components/appropriation-item/index.jsx +++ b/frontend/src/components/appropriation-item/index.jsx @@ -1,4 +1,4 @@ -import { Callout, HTMLTable, Tag } from "@blueprintjs/core"; +import { Callout, HTMLTable, Tag, Icon } from "@blueprintjs/core"; function AppropriationItem({ appropriation, onNavigate }) { return ( @@ -9,12 +9,14 @@ function AppropriationItem({ appropriation, onNavigate }) { onClick={() => onNavigate(appropriation.legislationContentId) } + style={{ cursor: "pointer" }} > {appropriation.parentId ? "Sub " : ""}Appropriation # {appropriation.appropriationId}{" "} {appropriation.newSpending && ( New Spending )} + } /> + } + /> + {bill2?.votes?.length > 0 && ( { + const { billVersionId, scrollContentIdIntoView, textTree } = useContext(BillContext); + const [effectiveDateActions, setEffectiveDateActions] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [contentLookup, setContentLookup] = useState({}); + + // Create a lookup map from legislation_content_id to content_str + useEffect(() => { + if (!textTree || !textTree.children) { + return; + } + + const lookup = {}; + + const buildLookup = (node) => { + if (node.legislation_content_id && node.content_str) { + lookup[node.legislation_content_id] = node.content_str; + } + + if (node.children) { + node.children.forEach(buildLookup); + } + }; + + buildLookup(textTree); + setContentLookup(lookup); + console.log(lookup); + }, [textTree]); + + useEffect(() => { + if (!billVersionId) { + setLoading(false); + return; + } + + setLoading(true); + setError(null); + + getBillActionsv2(billVersionId) + .then((actions) => { + if (!actions || !Array.isArray(actions)) { + setEffectiveDateActions([]); + setLoading(false); + return; + } + + // Filter actions to only include those with EFFECTIVE-DATE actions + const effectiveActions = actions.filter(action => { + const actionData = action.actions?.[0]; + return actionData && actionData["EFFECTIVE-DATE"]; + }); + + setEffectiveDateActions(effectiveActions); + setLoading(false); + }) + .catch((err) => { + console.error("Error fetching effective date actions:", err); + setError(err); + setLoading(false); + }); + }, [billVersionId]); + + if (loading) { + return ; + } + + if (error) { + return ( +

+ Error loading effective date actions. Please try again later. +

+ ); + } + + if (!billVersionId) { + return ( +

+ Bill version information is not available. +

+ ); + } + + return effectiveDateActions.length > 0 ? ( + <> + {effectiveDateActions.map((action) => ( + + ))} + + ) : ( +

+ There are no effective date actions parsed in the contents of this + bill. +

+ ); +}; + +export default EffectiveDateActions; \ No newline at end of file diff --git a/frontend/src/components/effective-date-item/index.jsx b/frontend/src/components/effective-date-item/index.jsx new file mode 100644 index 00000000..0583e97e --- /dev/null +++ b/frontend/src/components/effective-date-item/index.jsx @@ -0,0 +1,121 @@ +import { Callout, HTMLTable, Tag, Icon } from "@blueprintjs/core"; + +function EffectiveDateItem({ action, onNavigate, contentLookup }) { + const actions = action.actions?.[0] || {}; + const effectiveAction = actions["EFFECTIVE-DATE"]; + + if (!effectiveAction) { + return null; + } + + const formatTimeframe = (amount, unit) => { + if (amount === "0") { + return "Immediately upon enactment"; + } + const unitDisplay = amount === "1" ? unit.replace(/s$/, "") : unit; + return `${amount} ${unitDisplay} after enactment`; + }; + + const getActionDescription = () => { + if (effectiveAction.effective_date) { + return `Takes effect on ${effectiveAction.effective_date}`; + } + if (effectiveAction.amount && effectiveAction.unit) { + return formatTimeframe(effectiveAction.amount, effectiveAction.unit); + } + return "Takes effect as specified"; + }; + + const getTargetDescription = () => { + if (effectiveAction.target) { + return effectiveAction.target; + } + return "General provision"; + }; + + const getContentText = () => { + if (!contentLookup || !action.legislationContentId) { + return null; + } + return contentLookup[action.legislationContentId]; + }; + + const contentText = getContentText(); + + return ( + <> + +

onNavigate && onNavigate(action.legislationContentId)} + style={{ cursor: "pointer" }} + > + Effective Date Action{" "} + {effectiveAction.amount === "0" && ( + Immediate + )} + {effectiveAction.sunset_date && ( + Has Sunset + )} +

+ + + + + Applies To + + {getTargetDescription()} + + + + Effective Date + + {getActionDescription()} + + {effectiveAction.effective_date && ( + + + Specific Date + + {effectiveAction.effective_date} + + )} + {effectiveAction.sunset_date && ( + + + Sunset Date + + {effectiveAction.sunset_date} + + )} + {effectiveAction.trigger_date && ( + + + Trigger + + {effectiveAction.trigger_date} + + )} + {contentText && ( + + + Content + + + {contentText} + + + )} + + +
+
+ + ); +} + +export default EffectiveDateItem; \ No newline at end of file diff --git a/frontend/src/components/index.js b/frontend/src/components/index.js index e28245d6..0b1bcb6c 100644 --- a/frontend/src/components/index.js +++ b/frontend/src/components/index.js @@ -11,6 +11,8 @@ export { default as BillVersionsBreadcrumb } from "./bill-versions-breadcrumb"; export { default as BillViewSidebar } from "./bill-view-sidebar"; export { default as BillViewToolbar } from "./bill-view-toolbar"; export { default as CollapsibleSection } from "./collapsible-section"; +export { default as EffectiveDateActions } from "./effective-date-actions"; +export { default as EffectiveDateItem } from "./effective-date-item"; export { default as LegislatorChip } from "./legislator-chip"; export { default as LegislatorProfile } from "./legislator-profile"; export { default as Paginator } from "./paginator"; diff --git a/frontend/src/context/bill.jsx b/frontend/src/context/bill.jsx deleted file mode 100644 index b0a0c68b..00000000 --- a/frontend/src/context/bill.jsx +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from "react"; - -export const BillContext = createContext(); diff --git a/frontend/src/context/bill.tsx b/frontend/src/context/bill.tsx new file mode 100644 index 00000000..5c706e87 --- /dev/null +++ b/frontend/src/context/bill.tsx @@ -0,0 +1,120 @@ +import React, { createContext } from "react"; + +// Type definitions for the BillContext + +export interface LegislationVersion { + legislation_version_id: number; + legislation_version: string; + effective_date?: string; + created_at?: string; +} + +export interface LegislationAction { + actionDate: string; + text: string; + sourceName: string; + actionCode?: string; + sourceCode?: string; +} + +export interface LegislationVote { + question: string; + datetime: string; + passed: boolean; + // Add other vote properties as needed +} + +export interface Sponsor { + // Define sponsor properties based on your data structure + [key: string]: any; +} + +export interface Appropriation { + appropriationId: number; + parentId?: number; + amount: number; + fiscalYears: number[]; + untilExpended: boolean; + newSpending: boolean; + briefPurpose: string; + expirationYear?: number; + legislationContentId: number; +} + +export interface Bill { + legislation_id: number; + title: string; + congress: number; + chamber: string; + number: number; + legislation_versions: LegislationVersion[]; + usc_release_id?: string | number; + effective_date?: string; +} + +export interface Bill2 { + sponsor?: Sponsor; + actions?: LegislationAction[]; + votes?: LegislationVote[]; + appropriations?: Appropriation[]; +} + +export interface BillSummary { + summary: string; + // Add other summary properties as needed +} + +export interface TextTreeNode { + legislation_content_id: number; + lc_ident?: string; + content_str?: string; + section_display?: string; + heading?: string; + content_type?: string; + children?: TextTreeNode[]; +} + +export interface TextTree extends TextTreeNode { + loading?: boolean; +} + +export interface DateAnchor { + title: string; + hash: string; +} + +export interface BillContextValue { + // Core bill data + bill: Bill; + bill2: Bill2; + billEffective: string | null; + billNumber: number; + billSummary: BillSummary[]; + billVers: string; + billVersion: string; + billVersionId: number; + + // URL/routing data + chamber: string; + congress: number; + + // Content structure + textTree: TextTree; + dateAnchors: DateAnchor[]; + + // Functions + scrollContentIdIntoView: (contentId: number) => void; + setBillVers: (version: string) => void; +} + +// Create context with proper typing +export const BillContext = createContext(undefined); + +// Custom hook for using BillContext with type safety +export const useBillContext = (): BillContextValue => { + const context = React.useContext(BillContext); + if (context === undefined) { + throw new Error('useBillContext must be used within a BillContext.Provider'); + } + return context; +}; \ No newline at end of file diff --git a/frontend/src/context/index.js b/frontend/src/context/index.js index cb1e5e7a..bec19c75 100644 --- a/frontend/src/context/index.js +++ b/frontend/src/context/index.js @@ -1,4 +1,4 @@ -export { BillContext } from "./bill"; +export { BillContext } from "./bill.tsx"; export { PreferenceContext, PreferenceEnum, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0224017f..aa79c439 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2631,6 +2631,11 @@ dependencies: undici-types "~5.26.4" +"@types/node@^16.0.0": + version "16.18.126" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.126.tgz#27875faa2926c0f475b39a8bb1e546c0176f8d4b" + integrity sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw== + "@types/parse-json@^4.0.0": version "4.0.2" resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz" @@ -2673,6 +2678,11 @@ dependencies: "@types/react" "*" +"@types/react-dom@^18.2.0": + version "18.3.7" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.7.tgz#b89ddf2cd83b4feafcc4e2ea41afdfb95a0d194f" + integrity sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ== + "@types/react@*": version "18.2.45" resolved "https://registry.npmjs.org/@types/react/-/react-18.2.45.tgz" @@ -2689,6 +2699,14 @@ dependencies: csstype "^3.0.2" +"@types/react@^18.2.0": + version "18.3.23" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.23.tgz#86ae6f6b95a48c418fecdaccc8069e0fbb63696a" + integrity sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + "@types/resolve@1.17.1": version "1.17.1" resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz" @@ -11041,6 +11059,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typescript@^5.0.0: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz"