diff --git a/.github/actions/get-node-version/src/main.ts b/.github/actions/get-node-version/src/main.ts index 2bf96a08b..c7e73d66c 100644 --- a/.github/actions/get-node-version/src/main.ts +++ b/.github/actions/get-node-version/src/main.ts @@ -1,6 +1,6 @@ -import { setOutput, setFailed, getInput } from "@actions/core"; -import semver from "semver"; +import { getInput, setFailed, setOutput } from "@actions/core"; import { readFileSync } from "fs"; +import semver from "semver"; (() => { const inputPath = getInput("package-json"); diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6498990e0..559589aa3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,6 +13,9 @@ repos: hooks: - id: prettier exclude_types: [markdown, yaml] + additional_dependencies: + - prettier@3.1.0 + - "@trivago/prettier-plugin-sort-imports@6.0.0" - repo: https://github.com/thibaudcolas/pre-commit-stylelint rev: v16.9.0 hooks: diff --git a/.prettierrc b/.prettierrc index d0a82a77b..a438dc8f5 100644 --- a/.prettierrc +++ b/.prettierrc @@ -15,5 +15,14 @@ "tabWidth": 2, "trailingComma": "es5", "useTabs": false, - "vueIndentScriptAndStyle": false + "vueIndentScriptAndStyle": false, + "plugins": ["@trivago/prettier-plugin-sort-imports"], + "importOrder": [ + "^\\./styles/index\\.css$", + "", + "^@thunderstore/(.*)$", + "^[./]" + ], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true } diff --git a/README.md b/README.md index b7dfb5f8e..ae22b3f66 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,25 @@ running: docker compose -f docker-compose.build.yml build ``` +## Testing (Docker) + +Frontend tests run in Vitest browser mode (Playwright). To keep the environment consistent, use the dedicated test runner compose file instead of the dev container. + +Prereqs: +- Ensure `./build-secrets/.npmrc` exists (same requirement as Docker builds). + +Run tests: + +```bash +yarn test:container +``` + +Run coverage: + +```bash +yarn coverage:container +``` + ## pre-commit [Pre-commit](https://pre-commit.com/) enforces code style practices in this diff --git a/apps/cyberstorm-remix/README.md b/apps/cyberstorm-remix/README.md index 746182020..4224ae26a 100644 --- a/apps/cyberstorm-remix/README.md +++ b/apps/cyberstorm-remix/README.md @@ -30,8 +30,8 @@ The easiest way to run the full stack (Backend + Frontend) is using Docker. (If you have some pre-existing containers, please do `docker compose -f docker-compose.remix.development.yml down -v` and `docker compose -f docker-compose.remix.development.yml up -d --build`) 4. **Open Browser** - - **Frontend**: [http://new.localhost](http://new.localhost) - - **Backend**: [http://localhost](http://localhost) + - **Frontend**: [http://new.thunderstore.localhost](http://new.thunderstore.localhost) + - **Backend**: [http://thunderstore.localhost](http://thunderstore.localhost) ## Manual Setup diff --git a/apps/cyberstorm-remix/app/c/community.tsx b/apps/cyberstorm-remix/app/c/community.tsx index 16e18e84c..d30ced32e 100644 --- a/apps/cyberstorm-remix/app/c/community.tsx +++ b/apps/cyberstorm-remix/app/c/community.tsx @@ -1,3 +1,12 @@ +import { faDiscord } from "@fortawesome/free-brands-svg-icons"; +import { faBook, faDownload } from "@fortawesome/free-solid-svg-icons"; +import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { Suspense } from "react"; import type { LoaderFunctionArgs, ShouldRevalidateFunctionArgs, @@ -9,6 +18,7 @@ import { useLocation, useOutletContext, } from "react-router"; + import { Heading, NewButton, @@ -16,19 +26,11 @@ import { NewLink, SkeletonBox, } from "@thunderstore/cyberstorm"; -import "./Community.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faBook, faDownload } from "@fortawesome/free-solid-svg-icons"; -import { faDiscord } from "@fortawesome/free-brands-svg-icons"; -import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons"; +import { classnames } from "@thunderstore/cyberstorm"; import { DapperTs } from "@thunderstore/dapper-ts"; + import { type OutletContextShape } from "../root"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; -import { Suspense } from "react"; -import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; +import "./Community.css"; export async function loader({ params }: LoaderFunctionArgs) { if (params.communityId) { diff --git a/apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx b/apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx index 8490954a7..ac0d7a502 100644 --- a/apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx +++ b/apps/cyberstorm-remix/app/c/tabs/PackageSearch/PackageSearch.tsx @@ -1,12 +1,14 @@ -import { useLoaderData, useOutletContext } from "react-router"; -import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch"; -import { PackageOrderOptions } from "~/commonComponents/PackageSearch/components/PackageOrder"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; +import { useLoaderData, useOutletContext } from "react-router"; +import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch"; +import { PackageOrderOptions } from "~/commonComponents/PackageSearch/components/PackageOrder"; import { type OutletContextShape } from "~/root"; + +import { DapperTs } from "@thunderstore/dapper-ts"; + import type { Route } from "./+types/PackageSearch"; export async function loader({ params, request }: Route.LoaderArgs) { diff --git a/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.tsx b/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.tsx index f6d6cd3ce..1b1efbc9c 100644 --- a/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/CheckboxList/CheckboxList.tsx @@ -4,12 +4,13 @@ import { faSquareCheck, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { memo } from "react"; +import { type TRISTATE } from "~/commonComponents/types"; -import "./CheckboxList.css"; import { Actionable, NewIcon } from "@thunderstore/cyberstorm"; -import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; -import { type TRISTATE } from "~/commonComponents/types"; -import { memo } from "react"; +import { classnames } from "@thunderstore/cyberstorm"; + +import "./CheckboxList.css"; type typeA = (v: B) => void; diff --git a/apps/cyberstorm-remix/app/commonComponents/CodeBoxHTML/CodeBoxHTML.tsx b/apps/cyberstorm-remix/app/commonComponents/CodeBoxHTML/CodeBoxHTML.tsx index c29aaf62e..8b540b52d 100644 --- a/apps/cyberstorm-remix/app/commonComponents/CodeBoxHTML/CodeBoxHTML.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/CodeBoxHTML/CodeBoxHTML.tsx @@ -1,6 +1,8 @@ -import { CodeBox, NewAlert } from "@thunderstore/cyberstorm"; -import { memo, useMemo } from "react"; import { stripHtmlTags } from "cyberstorm/utils/HTMLParsing"; +import { memo, useMemo } from "react"; + +import { CodeBox, NewAlert } from "@thunderstore/cyberstorm"; + import "./CodeBoxHTML.css"; export interface CodeBoxHTMLProps { diff --git a/apps/cyberstorm-remix/app/commonComponents/Collapsible/Collapsible.tsx b/apps/cyberstorm-remix/app/commonComponents/Collapsible/Collapsible.tsx index babfcc1ae..fad4c28ed 100644 --- a/apps/cyberstorm-remix/app/commonComponents/Collapsible/Collapsible.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/Collapsible/Collapsible.tsx @@ -1,9 +1,11 @@ -import { memo, type PropsWithChildren } from "react"; import { faCaretDown } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import "./Collapsible.css"; +import { type PropsWithChildren, memo } from "react"; + import { NewIcon } from "@thunderstore/cyberstorm"; -import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; +import { classnames } from "@thunderstore/cyberstorm"; + +import "./Collapsible.css"; interface Props extends PropsWithChildren { headerTitle: string; diff --git a/apps/cyberstorm-remix/app/commonComponents/CollapsibleText/CollapsibleText.tsx b/apps/cyberstorm-remix/app/commonComponents/CollapsibleText/CollapsibleText.tsx index e5086f8b3..66a3be644 100644 --- a/apps/cyberstorm-remix/app/commonComponents/CollapsibleText/CollapsibleText.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/CollapsibleText/CollapsibleText.tsx @@ -1,6 +1,8 @@ -import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; +import { type ReactElement, useState } from "react"; + +import { classnames } from "@thunderstore/cyberstorm"; + import "./CollapsibleText.css"; -import { useState, type ReactElement } from "react"; export interface CollapsibleTextProps { text?: string; diff --git a/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.tsx b/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.tsx index 51d925530..d7f3dc56b 100644 --- a/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.tsx @@ -1,8 +1,10 @@ -import "./Connection.css"; +import { type ReactElement } from "react"; + import { NewIcon, NewSwitch } from "@thunderstore/cyberstorm"; import { type OAuthConnection } from "@thunderstore/dapper/types"; -import { type ReactElement } from "react"; -import { type userLinkedAccountDisconnectProviders } from "../../../../../packages/thunderstore-api/src"; +import type { userLinkedAccountDisconnectProviders } from "@thunderstore/thunderstore-api"; + +import "./Connection.css"; interface ConnectionProps { name: string; diff --git a/apps/cyberstorm-remix/app/commonComponents/CopyButton/CopyButton.tsx b/apps/cyberstorm-remix/app/commonComponents/CopyButton/CopyButton.tsx index 66c58ae90..0e2b50058 100644 --- a/apps/cyberstorm-remix/app/commonComponents/CopyButton/CopyButton.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/CopyButton/CopyButton.tsx @@ -1,10 +1,12 @@ +import { faCheck, faClone } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faClone, faCheck } from "@fortawesome/free-solid-svg-icons"; import { type Dispatch, type SetStateAction, useState } from "react"; -import "./CopyButton.css"; -import { NewIcon, Tooltip } from "@thunderstore/cyberstorm/src"; import React from "react"; +import { NewIcon, Tooltip } from "@thunderstore/cyberstorm"; + +import "./CopyButton.css"; + interface CopyButtonProps { text: string; } diff --git a/apps/cyberstorm-remix/app/commonComponents/ErrorBoundary/RouteErrorBoundary.tsx b/apps/cyberstorm-remix/app/commonComponents/ErrorBoundary/RouteErrorBoundary.tsx index 001aaf38b..be7a02e90 100644 --- a/apps/cyberstorm-remix/app/commonComponents/ErrorBoundary/RouteErrorBoundary.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/ErrorBoundary/RouteErrorBoundary.tsx @@ -1,6 +1,6 @@ -import { useEffect, type JSX } from "react"; -import { isRouteErrorResponse, useRouteError } from "react-router"; import { captureRemixErrorBoundaryError } from "@sentry/remix"; +import { type JSX, useEffect } from "react"; +import { isRouteErrorResponse, useRouteError } from "react-router"; import { ApiError } from "@thunderstore/thunderstore-api"; diff --git a/apps/cyberstorm-remix/app/commonComponents/Footer/Footer.tsx b/apps/cyberstorm-remix/app/commonComponents/Footer/Footer.tsx index 37318e8e1..189b698ec 100644 --- a/apps/cyberstorm-remix/app/commonComponents/Footer/Footer.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/Footer/Footer.tsx @@ -1,7 +1,8 @@ -import "./Footer.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faDiscord, faGithub } from "@fortawesome/free-brands-svg-icons"; import { faBoltLightning } from "@fortawesome/free-solid-svg-icons"; +import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + import { Heading, NewButton, @@ -9,7 +10,8 @@ import { NewLink, ThunderstoreLogoHorizontal, } from "@thunderstore/cyberstorm"; -import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons"; + +import "./Footer.css"; const AD_IMAGE_SRC = "/cyberstorm-static/images/tsmm_screenshot.png"; const DISCORD_URL = "https://discord.thunderstore.io/"; @@ -200,7 +202,7 @@ export function Footer() {

- © 2025 Thunderstore and contributors.{" "} + © 2026 Thunderstore and contributors.{" "} This page is{" "} + ); +}); + +RequiredIndicator.displayName = "RequiredIndicator"; diff --git a/apps/cyberstorm-remix/app/commonComponents/StalenessIndicator/StalenessIndicator.tsx b/apps/cyberstorm-remix/app/commonComponents/StalenessIndicator/StalenessIndicator.tsx index 05e699e0f..e9eea8e00 100644 --- a/apps/cyberstorm-remix/app/commonComponents/StalenessIndicator/StalenessIndicator.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/StalenessIndicator/StalenessIndicator.tsx @@ -1,9 +1,11 @@ -import "./StalenessIndicator.css"; -import { type ReactNode } from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faSpinnerThird } from "@fortawesome/pro-solid-svg-icons"; -import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { type ReactNode } from "react"; + import { NewIcon } from "@thunderstore/cyberstorm"; +import { classnames } from "@thunderstore/cyberstorm"; + +import "./StalenessIndicator.css"; interface Props { children: ReactNode; diff --git a/apps/cyberstorm-remix/app/communities/communities.tsx b/apps/cyberstorm-remix/app/communities/communities.tsx index df3a45629..8c89d10cc 100644 --- a/apps/cyberstorm-remix/app/communities/communities.tsx +++ b/apps/cyberstorm-remix/app/communities/communities.tsx @@ -1,34 +1,36 @@ -import type { LoaderFunctionArgs, MetaFunction } from "react-router"; -import { - CardCommunity, - EmptyState, - NewTextInput, - NewSelect, - SkeletonBox, -} from "@thunderstore/cyberstorm"; -import "./Communities.css"; -import { useState, useEffect, useRef, memo, Suspense } from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { - faSearch, faArrowDownAZ, + faSearch, faStar, } from "@fortawesome/free-solid-svg-icons"; -import { faGhost, faFire } from "@fortawesome/free-solid-svg-icons"; -import { useDebounce } from "use-debounce"; +import { faFire, faGhost } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { Suspense, memo, useEffect, useRef, useState } from "react"; +import type { LoaderFunctionArgs, MetaFunction } from "react-router"; import { Await, useLoaderData, useNavigationType, useSearchParams, } from "react-router"; -import type { Communities } from "@thunderstore/dapper/types"; -import { DapperTs } from "@thunderstore/dapper-ts"; +import { useDebounce } from "use-debounce"; import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; + import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; + CardCommunity, + EmptyState, + NewSelect, + NewTextInput, + SkeletonBox, +} from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; +import type { Communities } from "@thunderstore/dapper/types"; + +import "./Communities.css"; export const meta: MetaFunction = () => { return [ diff --git a/apps/cyberstorm-remix/app/entry.client.tsx b/apps/cyberstorm-remix/app/entry.client.tsx index e66019b0b..9db99194b 100644 --- a/apps/cyberstorm-remix/app/entry.client.tsx +++ b/apps/cyberstorm-remix/app/entry.client.tsx @@ -1,15 +1,14 @@ import * as Sentry from "@sentry/remix"; -import { useEffect, startTransition, StrictMode } from "react"; -import { hydrateRoot } from "react-dom/client"; -import { useLocation, useMatches } from "react-router"; -import { HydratedRouter } from "react-router/dom"; - import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; -import { denyUrls } from "cyberstorm/utils/sentry"; import { initializeClientDapper } from "cyberstorm/utils/dapperSingleton"; +import { denyUrls } from "cyberstorm/utils/sentry"; +import { StrictMode, startTransition, useEffect } from "react"; +import { hydrateRoot } from "react-dom/client"; +import { useLocation, useMatches } from "react-router"; +import { HydratedRouter } from "react-router/dom"; const publicEnvVariables = getPublicEnvVariables([ "VITE_SITE_URL", diff --git a/apps/cyberstorm-remix/app/entry.server.tsx b/apps/cyberstorm-remix/app/entry.server.tsx index 88527f2b8..b937ad247 100644 --- a/apps/cyberstorm-remix/app/entry.server.tsx +++ b/apps/cyberstorm-remix/app/entry.server.tsx @@ -1,12 +1,10 @@ -import { PassThrough } from "node:stream"; - -import type { AppLoadContext, EntryContext } from "react-router"; import { createReadableStreamFromReadable } from "@react-router/node"; -import { ServerRouter } from "react-router"; +import * as Sentry from "@sentry/remix"; import * as isbotModule from "isbot"; +import { PassThrough } from "node:stream"; import { renderToPipeableStream } from "react-dom/server"; - -import * as Sentry from "@sentry/remix"; +import type { AppLoadContext, EntryContext } from "react-router"; +import { ServerRouter } from "react-router"; Sentry.init({ dsn: process.env.SENTRY_DSN, diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageButton.tsx b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageButton.tsx index 8a9480f90..22714c6ad 100644 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageButton.tsx +++ b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageButton.tsx @@ -1,6 +1,7 @@ -import { NewButton, NewIcon } from "@thunderstore/cyberstorm"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faFlagSwallowtail } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { NewButton, NewIcon } from "@thunderstore/cyberstorm"; export function ReportPackageButton(props: { onClick: () => void }) { return ( diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageForm.tsx b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageForm.tsx index 15a637f84..56058fdc7 100644 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageForm.tsx +++ b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageForm.tsx @@ -1,3 +1,5 @@ +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; + import { Modal, NewAlert, @@ -7,14 +9,12 @@ import { type SelectOption, } from "@thunderstore/cyberstorm"; import { - type RequestConfig, type PackageListingReportRequestData, - packageListingReport, + type RequestConfig, isApiError, + packageListingReport, } from "@thunderstore/thunderstore-api"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; - const reportOptions: SelectOption[] = [ { value: "Spam", label: "Spam" }, diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/useReportPackage.tsx b/apps/cyberstorm-remix/app/p/components/ReportPackage/useReportPackage.tsx index ce9a5d413..581a4596c 100644 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/useReportPackage.tsx +++ b/apps/cyberstorm-remix/app/p/components/ReportPackage/useReportPackage.tsx @@ -1,16 +1,17 @@ import { useCallback, useEffect, useState } from "react"; +import { + type PackageListingReportRequestData, + type RequestConfig, +} from "@thunderstore/thunderstore-api"; + +import { ReportPackageButton } from "./ReportPackageButton"; import { ReportPackageForm, type ReportPackageFormProps, } from "./ReportPackageForm"; -import { ReportPackageButton } from "./ReportPackageButton"; import { ReportPackageModal } from "./ReportPackageModal"; import { ReportPackageSubmitted } from "./ReportPackageSubmitted"; -import { - type PackageListingReportRequestData, - type RequestConfig, -} from "@thunderstore/thunderstore-api"; const createInitialFormInputs = (): PackageListingReportRequestData => ({ reason: "Other", diff --git a/apps/cyberstorm-remix/app/p/components/TeamMembers/TeamMembers.tsx b/apps/cyberstorm-remix/app/p/components/TeamMembers/TeamMembers.tsx index e8906cdcb..52ef231b3 100644 --- a/apps/cyberstorm-remix/app/p/components/TeamMembers/TeamMembers.tsx +++ b/apps/cyberstorm-remix/app/p/components/TeamMembers/TeamMembers.tsx @@ -1,12 +1,14 @@ -import { Heading, NewAvatar, NewIcon, NewLink } from "@thunderstore/cyberstorm"; import { faCaretRight, faCrown } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import "./TeamMembers.css"; + +import { Heading, NewAvatar, NewIcon, NewLink } from "@thunderstore/cyberstorm"; import { type PackageListingDetails, type TeamMember, } from "@thunderstore/dapper/types"; +import "./TeamMembers.css"; + export default function TeamMembers(props: { listing: PackageListingDetails; domain: string; diff --git a/apps/cyberstorm-remix/app/p/dependants/Dependants.tsx b/apps/cyberstorm-remix/app/p/dependants/Dependants.tsx index bd46139a3..594017ea2 100644 --- a/apps/cyberstorm-remix/app/p/dependants/Dependants.tsx +++ b/apps/cyberstorm-remix/app/p/dependants/Dependants.tsx @@ -1,21 +1,23 @@ +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { Suspense } from "react"; import { Await, useLoaderData, useOutletContext } from "react-router"; +import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch"; +import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; + import { - formatToDisplayName, NewLink, SkeletonBox, + formatToDisplayName, } from "@thunderstore/cyberstorm"; -import "./Dependants.css"; -import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch"; import { DapperTs } from "@thunderstore/dapper-ts"; + import { PackageOrderOptions } from "../../commonComponents/PackageSearch/components/PackageOrder"; import { type OutletContextShape } from "../../root"; -import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; import type { Route } from "./+types/Dependants"; -import { Suspense } from "react"; +import "./Dependants.css"; export async function loader({ params, request }: Route.LoaderArgs) { if (params.communityId && params.packageId && params.namespaceId) { diff --git a/apps/cyberstorm-remix/app/p/packageEdit.tsx b/apps/cyberstorm-remix/app/p/packageEdit.tsx index d28203b68..cab99f62e 100644 --- a/apps/cyberstorm-remix/app/p/packageEdit.tsx +++ b/apps/cyberstorm-remix/app/p/packageEdit.tsx @@ -1,5 +1,16 @@ +import { faBan, faCheck } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { useReducer } from "react"; import type { LoaderFunctionArgs, MetaFunction } from "react-router"; import { useLoaderData, useOutletContext, useRevalidator } from "react-router"; +import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; +import { type OutletContextShape } from "~/root"; + import { NewAlert, NewButton, @@ -9,27 +20,18 @@ import { formatToDisplayName, useToast, } from "@thunderstore/cyberstorm"; -import "./packageEdit.css"; +import { DapperTs } from "@thunderstore/dapper-ts"; import { ApiError, + type PackageListingUpdateRequestData, packageDeprecate, packageListingUpdate, - type PackageListingUpdateRequestData, packageUnlist, } from "@thunderstore/thunderstore-api"; -import { DapperTs } from "@thunderstore/dapper-ts"; -import { type OutletContextShape } from "~/root"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; -import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; -import { useReducer } from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faBan, faCheck } from "@fortawesome/pro-solid-svg-icons"; import { ApiAction } from "@thunderstore/ts-api-react-actions"; +import "./packageEdit.css"; + export const meta: MetaFunction = ({ data }) => { return [ { diff --git a/apps/cyberstorm-remix/app/p/packageListing.tsx b/apps/cyberstorm-remix/app/p/packageListing.tsx index 6f0724c69..3508b59c3 100644 --- a/apps/cyberstorm-remix/app/p/packageListing.tsx +++ b/apps/cyberstorm-remix/app/p/packageListing.tsx @@ -1,7 +1,29 @@ import { - memo, + faCaretRight, + faCog, + faDownload, + faHandHoldingHeart, + faScaleBalanced, + faThumbsUp, + faUsers, + faWarning, +} from "@fortawesome/free-solid-svg-icons"; +import { faArrowUpRight, faLips } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { CopyButton } from "app/commonComponents/CopyButton/CopyButton"; +import { PageHeader } from "app/commonComponents/PageHeader/PageHeader"; +import { useReportPackage } from "app/p/components/ReportPackage/useReportPackage"; +import TeamMembers from "app/p/components/TeamMembers/TeamMembers"; +import { type OutletContextShape } from "app/root"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { isPromise } from "cyberstorm/utils/typeChecks"; +import { type ReactElement, Suspense, + memo, useEffect, useMemo, useRef, @@ -9,37 +31,14 @@ import { } from "react"; import { Await, + type LoaderFunctionArgs, Outlet, + type ShouldRevalidateFunctionArgs, useLoaderData, useLocation, useOutletContext, - type LoaderFunctionArgs, - type ShouldRevalidateFunctionArgs, } from "react-router"; import { useHydrated } from "remix-utils/use-hydrated"; -import { - faUsers, - faHandHoldingHeart, - faDownload, - faThumbsUp, - faWarning, - faCaretRight, - faScaleBalanced, - faCog, -} from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faArrowUpRight, faLips } from "@fortawesome/pro-solid-svg-icons"; - -import { CopyButton } from "app/commonComponents/CopyButton/CopyButton"; -import { PageHeader } from "app/commonComponents/PageHeader/PageHeader"; -import TeamMembers from "app/p/components/TeamMembers/TeamMembers"; -import { useReportPackage } from "app/p/components/ReportPackage/useReportPackage"; -import { type OutletContextShape } from "app/root"; -import { isPromise } from "cyberstorm/utils/typeChecks"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; import { Drawer, @@ -61,14 +60,14 @@ import { useToast, } from "@thunderstore/cyberstorm"; import { PackageLikeAction } from "@thunderstore/cyberstorm-forms"; -import type { TagVariants } from "@thunderstore/cyberstorm-theme/src/components"; -import type { CurrentUser } from "@thunderstore/dapper/types"; +import type { TagVariants } from "@thunderstore/cyberstorm-theme"; import { DapperTs, type DapperTsInterface } from "@thunderstore/dapper-ts"; +import type { CurrentUser } from "@thunderstore/dapper/types"; import { + type RequestConfig, fetchPackagePermissions, packageListingApprove, packageListingReject, - type RequestConfig, } from "@thunderstore/thunderstore-api"; import { ApiAction } from "@thunderstore/ts-api-react-actions"; diff --git a/apps/cyberstorm-remix/app/p/packageVersion.tsx b/apps/cyberstorm-remix/app/p/packageVersion.tsx index 2502d75cd..6ab2c0e15 100644 --- a/apps/cyberstorm-remix/app/p/packageVersion.tsx +++ b/apps/cyberstorm-remix/app/p/packageVersion.tsx @@ -1,3 +1,25 @@ +import { + faCaretRight, + faDownload, + faHandHoldingHeart, + faUsers, +} from "@fortawesome/free-solid-svg-icons"; +import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { isPromise } from "cyberstorm/utils/typeChecks"; +import { + type ReactElement, + Suspense, + memo, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import type { LoaderFunctionArgs, ShouldRevalidateFunctionArgs, @@ -9,6 +31,11 @@ import { useLocation, useOutletContext, } from "react-router"; +import { useHydrated } from "remix-utils/use-hydrated"; +import { CopyButton } from "~/commonComponents/CopyButton/CopyButton"; +import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; +import { type OutletContextShape } from "~/root"; + import { Drawer, Heading, @@ -16,46 +43,21 @@ import { NewButton, NewIcon, NewLink, + RelativeTime, SkeletonBox, Tabs, -} from "@thunderstore/cyberstorm"; -import "./packageListing.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { ThunderstoreLogo } from "@thunderstore/cyberstorm/src/svg/svg"; -import { - faUsers, - faHandHoldingHeart, - faDownload, - faCaretRight, -} from "@fortawesome/free-solid-svg-icons"; -import { - memo, - type ReactElement, - Suspense, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { useHydrated } from "remix-utils/use-hydrated"; -import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; -import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons"; -import { RelativeTime } from "@thunderstore/cyberstorm/src/components/RelativeTime/RelativeTime"; -import { + ThunderstoreLogo, formatFileSize, formatInteger, formatToDisplayName, -} from "@thunderstore/cyberstorm/src/utils/utils"; +} from "@thunderstore/cyberstorm"; import { DapperTs } from "@thunderstore/dapper-ts"; -import { type OutletContextShape } from "~/root"; -import { CopyButton } from "~/commonComponents/CopyButton/CopyButton"; import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; -import { getTeamDetails } from "@thunderstore/dapper-ts/src/methods/team"; -import { isPromise } from "cyberstorm/utils/typeChecks"; -import { getPackageVersionDetails } from "@thunderstore/dapper-ts/src/methods/packageVersion"; + getPackageVersionDetails, + getTeamDetails, +} from "@thunderstore/dapper-ts"; + +import "./packageListing.css"; export async function loader({ params }: LoaderFunctionArgs) { if ( diff --git a/apps/cyberstorm-remix/app/p/packageVersionWithoutCommunity.tsx b/apps/cyberstorm-remix/app/p/packageVersionWithoutCommunity.tsx index 233a7a973..602e78bdb 100644 --- a/apps/cyberstorm-remix/app/p/packageVersionWithoutCommunity.tsx +++ b/apps/cyberstorm-remix/app/p/packageVersionWithoutCommunity.tsx @@ -1,3 +1,25 @@ +import { + faCaretRight, + faDownload, + faHandHoldingHeart, + faUsers, +} from "@fortawesome/free-solid-svg-icons"; +import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { isPromise } from "cyberstorm/utils/typeChecks"; +import { + type ReactElement, + Suspense, + memo, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import type { LoaderFunctionArgs, ShouldRevalidateFunctionArgs, @@ -9,6 +31,11 @@ import { useLocation, useOutletContext, } from "react-router"; +import { useHydrated } from "remix-utils/use-hydrated"; +import { CopyButton } from "~/commonComponents/CopyButton/CopyButton"; +import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; +import { type OutletContextShape } from "~/root"; + import { Drawer, Heading, @@ -16,46 +43,21 @@ import { NewButton, NewIcon, NewLink, + RelativeTime, SkeletonBox, Tabs, -} from "@thunderstore/cyberstorm"; -import "./packageListing.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { ThunderstoreLogo } from "@thunderstore/cyberstorm/src/svg/svg"; -import { - faUsers, - faHandHoldingHeart, - faDownload, - faCaretRight, -} from "@fortawesome/free-solid-svg-icons"; -import { - memo, - type ReactElement, - Suspense, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { useHydrated } from "remix-utils/use-hydrated"; -import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; -import { faArrowUpRight } from "@fortawesome/pro-solid-svg-icons"; -import { RelativeTime } from "@thunderstore/cyberstorm/src/components/RelativeTime/RelativeTime"; -import { + ThunderstoreLogo, formatFileSize, formatInteger, formatToDisplayName, -} from "@thunderstore/cyberstorm/src/utils/utils"; +} from "@thunderstore/cyberstorm"; import { DapperTs } from "@thunderstore/dapper-ts"; -import { type OutletContextShape } from "~/root"; -import { CopyButton } from "~/commonComponents/CopyButton/CopyButton"; import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; -import { getTeamDetails } from "@thunderstore/dapper-ts/src/methods/team"; -import { isPromise } from "cyberstorm/utils/typeChecks"; -import { getPackageVersionDetails } from "@thunderstore/dapper-ts/src/methods/packageVersion"; + getPackageVersionDetails, + getTeamDetails, +} from "@thunderstore/dapper-ts"; + +import "./packageListing.css"; export async function loader({ params }: LoaderFunctionArgs) { if (params.namespaceId && params.packageId && params.packageVersion) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Changelog/Changelog.tsx b/apps/cyberstorm-remix/app/p/tabs/Changelog/Changelog.tsx index 2ee474365..2b3c08777 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Changelog/Changelog.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Changelog/Changelog.tsx @@ -1,12 +1,14 @@ -import { Await, useLoaderData } from "react-router"; -import { type LoaderFunctionArgs } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; -import { SkeletonBox } from "@thunderstore/cyberstorm"; import { Suspense } from "react"; +import { Await, useLoaderData } from "react-router"; +import { type LoaderFunctionArgs } from "react-router"; + +import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; + import "./Changelog.css"; export async function loader({ params }: LoaderFunctionArgs) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Readme/PackageVersionReadme.tsx b/apps/cyberstorm-remix/app/p/tabs/Readme/PackageVersionReadme.tsx index 321be74c1..d0c5de923 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Readme/PackageVersionReadme.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Readme/PackageVersionReadme.tsx @@ -1,12 +1,14 @@ -import { Await, type LoaderFunctionArgs } from "react-router"; -import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; import { Suspense } from "react"; +import { Await, type LoaderFunctionArgs } from "react-router"; +import { useLoaderData } from "react-router"; + import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; + import "./Readme.css"; export async function loader({ params }: LoaderFunctionArgs) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Readme/PackageVersionWithoutCommunityReadme.tsx b/apps/cyberstorm-remix/app/p/tabs/Readme/PackageVersionWithoutCommunityReadme.tsx index 321be74c1..d0c5de923 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Readme/PackageVersionWithoutCommunityReadme.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Readme/PackageVersionWithoutCommunityReadme.tsx @@ -1,12 +1,14 @@ -import { Await, type LoaderFunctionArgs } from "react-router"; -import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; import { Suspense } from "react"; +import { Await, type LoaderFunctionArgs } from "react-router"; +import { useLoaderData } from "react-router"; + import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; + import "./Readme.css"; export async function loader({ params }: LoaderFunctionArgs) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Readme/Readme.tsx b/apps/cyberstorm-remix/app/p/tabs/Readme/Readme.tsx index 018f60e00..c8ca63f0d 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Readme/Readme.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Readme/Readme.tsx @@ -1,12 +1,14 @@ -import { Await, type LoaderFunctionArgs } from "react-router"; -import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; import { Suspense } from "react"; +import { Await, type LoaderFunctionArgs } from "react-router"; +import { useLoaderData } from "react-router"; + import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; + import "./Readme.css"; export async function loader({ params }: LoaderFunctionArgs) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Required/PackageVersionRequired.tsx b/apps/cyberstorm-remix/app/p/tabs/Required/PackageVersionRequired.tsx index bd8d99c21..396df1595 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Required/PackageVersionRequired.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Required/PackageVersionRequired.tsx @@ -1,13 +1,14 @@ -import { Suspense } from "react"; -import { type LoaderFunctionArgs } from "react-router"; -import { useLoaderData, Await } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; -import { SkeletonBox } from "@thunderstore/cyberstorm"; -import { PaginatedDependencies } from "~/commonComponents/PaginatedDependencies/PaginatedDependencies"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; +import { Suspense } from "react"; +import { type LoaderFunctionArgs } from "react-router"; +import { Await, useLoaderData } from "react-router"; +import { PaginatedDependencies } from "~/commonComponents/PaginatedDependencies/PaginatedDependencies"; + +import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; export async function loader({ params, request }: LoaderFunctionArgs) { if (params.namespaceId && params.packageId && params.packageVersion) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Required/PackageVersionWithoutCommunityRequired.tsx b/apps/cyberstorm-remix/app/p/tabs/Required/PackageVersionWithoutCommunityRequired.tsx index af6cdd0ac..b7c50ce3a 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Required/PackageVersionWithoutCommunityRequired.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Required/PackageVersionWithoutCommunityRequired.tsx @@ -1,13 +1,14 @@ -import { Suspense } from "react"; -import { type LoaderFunctionArgs } from "react-router"; -import { useLoaderData, Await } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; -import { SkeletonBox } from "@thunderstore/cyberstorm"; -import { PaginatedDependencies } from "~/commonComponents/PaginatedDependencies/PaginatedDependencies"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; +import { Suspense } from "react"; +import { type LoaderFunctionArgs } from "react-router"; +import { Await, useLoaderData } from "react-router"; +import { PaginatedDependencies } from "~/commonComponents/PaginatedDependencies/PaginatedDependencies"; + +import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; export async function loader({ params, request }: LoaderFunctionArgs) { if (params.namespaceId && params.packageId && params.packageVersion) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Required/Required.tsx b/apps/cyberstorm-remix/app/p/tabs/Required/Required.tsx index 64d76108c..2ab6b9bc3 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Required/Required.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Required/Required.tsx @@ -1,13 +1,14 @@ -import { Suspense } from "react"; -import { type LoaderFunctionArgs } from "react-router"; -import { useLoaderData, Await } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; -import { SkeletonBox } from "@thunderstore/cyberstorm"; -import { PaginatedDependencies } from "~/commonComponents/PaginatedDependencies/PaginatedDependencies"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; +import { Suspense } from "react"; +import { type LoaderFunctionArgs } from "react-router"; +import { Await, useLoaderData } from "react-router"; +import { PaginatedDependencies } from "~/commonComponents/PaginatedDependencies/PaginatedDependencies"; + +import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; export async function loader({ params, request }: LoaderFunctionArgs) { if (params.communityId && params.namespaceId && params.packageId) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Source/Source.tsx b/apps/cyberstorm-remix/app/p/tabs/Source/Source.tsx index 9b85b2ada..0d2661de4 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Source/Source.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Source/Source.tsx @@ -1,29 +1,29 @@ -import "./Source.css"; - +import { faClock, faDownload } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { Suspense } from "react"; import { Await, type LoaderFunctionArgs, useOutletContext } from "react-router"; import { useLoaderData } from "react-router"; -import { Suspense } from "react"; import ago from "s-ago"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faClock, faDownload } from "@fortawesome/free-solid-svg-icons"; +import { type OutletContextShape } from "~/root"; import { - SkeletonBox, + NewAlert as Alert, Heading, - NewIcon, NewButton, + NewIcon, + SkeletonBox, + TooltipWrapper, } from "@thunderstore/cyberstorm"; -import { TooltipWrapper } from "@thunderstore/cyberstorm/src/primitiveComponents/utils/utils"; -import { type OutletContextShape } from "~/root"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; import { DapperTs } from "@thunderstore/dapper-ts"; -import { Alert } from "@thunderstore/cyberstorm/src/newComponents/Alert/Alert"; +import { getPackageSource } from "@thunderstore/dapper-ts"; import { isApiError } from "@thunderstore/thunderstore-api"; -import { getPackageSource } from "@thunderstore/dapper-ts/src/methods/package"; + import { CodeBoxHTML } from "../../../commonComponents/CodeBoxHTML/CodeBoxHTML"; +import "./Source.css"; type PackageListingOutletContext = OutletContextShape & { packageDownloadUrl?: string; diff --git a/apps/cyberstorm-remix/app/p/tabs/Versions/PackageVersionVersions.tsx b/apps/cyberstorm-remix/app/p/tabs/Versions/PackageVersionVersions.tsx index 829d9e9e2..e629c1a1a 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Versions/PackageVersionVersions.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Versions/PackageVersionVersions.tsx @@ -1,22 +1,24 @@ -import "./Versions.css"; -import { - NewTableSort, - NewTable, - Heading, - SkeletonBox, - NewLink, -} from "@thunderstore/cyberstorm"; -import { Await, type LoaderFunctionArgs } from "react-router"; -import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; -import { Suspense } from "react"; -import { DownloadLink, InstallLink, ModManagerBanner } from "./common"; import { rowSemverCompare } from "cyberstorm/utils/semverCompare"; +import { Suspense } from "react"; +import { Await, type LoaderFunctionArgs } from "react-router"; +import { useLoaderData } from "react-router"; + +import { + Heading, + NewLink, + NewTable, + NewTableSort, + SkeletonBox, +} from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; + import { columns } from "./Versions"; +import "./Versions.css"; +import { DownloadLink, InstallLink, ModManagerBanner } from "./common"; export async function loader({ params }: LoaderFunctionArgs) { if (params.communityId && params.namespaceId && params.packageId) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Versions/PackageVersionWithoutCommunityVersions.tsx b/apps/cyberstorm-remix/app/p/tabs/Versions/PackageVersionWithoutCommunityVersions.tsx index 81b931dc9..dda4c7fc5 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Versions/PackageVersionWithoutCommunityVersions.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Versions/PackageVersionWithoutCommunityVersions.tsx @@ -1,22 +1,24 @@ -import "./Versions.css"; -import { - NewTableSort, - NewTable, - Heading, - SkeletonBox, - NewLink, -} from "@thunderstore/cyberstorm"; -import { Await, type LoaderFunctionArgs } from "react-router"; -import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; -import { Suspense } from "react"; -import { DownloadLink, InstallLink, ModManagerBanner } from "./common"; import { rowSemverCompare } from "cyberstorm/utils/semverCompare"; +import { Suspense } from "react"; +import { Await, type LoaderFunctionArgs } from "react-router"; +import { useLoaderData } from "react-router"; + +import { + Heading, + NewLink, + NewTable, + NewTableSort, + SkeletonBox, +} from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; + import { columns } from "./Versions"; +import "./Versions.css"; +import { DownloadLink, InstallLink, ModManagerBanner } from "./common"; export async function loader({ params }: LoaderFunctionArgs) { if (params.namespaceId && params.packageId) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.tsx b/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.tsx index 12d9be3fc..b34db13d8 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Versions/Versions.tsx @@ -1,22 +1,24 @@ -import "./Versions.css"; import { - NewTableSort, + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { rowSemverCompare } from "cyberstorm/utils/semverCompare"; +import { Suspense } from "react"; +import { Await, type LoaderFunctionArgs } from "react-router"; +import { useLoaderData } from "react-router"; + +import { + Heading, + NewLink, NewTable, type NewTableLabels, - Heading, + NewTableSort, SkeletonBox, - NewLink, } from "@thunderstore/cyberstorm"; -import { Await, type LoaderFunctionArgs } from "react-router"; -import { useLoaderData } from "react-router"; import { DapperTs } from "@thunderstore/dapper-ts"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; -import { Suspense } from "react"; + +import "./Versions.css"; import { DownloadLink, InstallLink, ModManagerBanner } from "./common"; -import { rowSemverCompare } from "cyberstorm/utils/semverCompare"; export async function loader({ params }: LoaderFunctionArgs) { if (params.communityId && params.namespaceId && params.packageId) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Versions/common.tsx b/apps/cyberstorm-remix/app/p/tabs/Versions/common.tsx index 965e754ae..41d215666 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Versions/common.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Versions/common.tsx @@ -1,9 +1,10 @@ import { faDownload } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { NewAlert, NewButton, NewIcon } from "@thunderstore/cyberstorm"; -import { ThunderstoreLogo } from "@thunderstore/cyberstorm/src/svg/svg"; import { memo } from "react"; +import { NewAlert, NewButton, NewIcon } from "@thunderstore/cyberstorm"; +import { ThunderstoreLogo } from "@thunderstore/cyberstorm"; + export const ModManagerBanner = memo(function ModManagerBanner() { return ( diff --git a/apps/cyberstorm-remix/app/p/tabs/Wiki/Wiki.tsx b/apps/cyberstorm-remix/app/p/tabs/Wiki/Wiki.tsx index 742346376..83907ac9f 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Wiki/Wiki.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Wiki/Wiki.tsx @@ -1,5 +1,10 @@ -import "./Wiki.css"; - +import { faPlus } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { Suspense } from "react"; import { Await, type LoaderFunctionArgs, @@ -7,18 +12,14 @@ import { useOutletContext, } from "react-router"; import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; -import { NewButton, NewIcon, SkeletonBox } from "@thunderstore/cyberstorm"; -import { faPlus } from "@fortawesome/pro-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { type OutletContextShape } from "~/root"; -import { Suspense } from "react"; -import { ApiError } from "../../../../../../packages/thunderstore-api/src"; -import { getPackageWiki } from "@thunderstore/dapper-ts/src/methods/package"; + +import { NewButton, NewIcon, SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; +import { getPackageWiki } from "@thunderstore/dapper-ts"; +import { ApiError } from "@thunderstore/thunderstore-api"; + +import "./Wiki.css"; export async function loader({ params }: LoaderFunctionArgs) { if (params.communityId && params.namespaceId && params.packageId) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiContent.tsx b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiContent.tsx index 285e7f826..a646e18af 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiContent.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiContent.tsx @@ -1,12 +1,3 @@ -import "./Wiki.css"; - -import { type PackageWikiPageResponseData } from "@thunderstore/thunderstore-api"; -import { - Heading, - NewButton, - NewIcon, - RelativeTime, -} from "@thunderstore/cyberstorm"; import { faArrowLeftLong, faArrowRightLong, @@ -14,9 +5,14 @@ import { faEdit, } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { memo, Suspense } from "react"; -import { Markdown } from "~/commonComponents/Markdown/Markdown"; +import { Suspense, memo } from "react"; import { Await } from "react-router"; +import { Markdown } from "~/commonComponents/Markdown/Markdown"; + +import { Heading, NewButton, NewIcon, RelativeTime } from "@thunderstore/cyberstorm"; +import { type PackageWikiPageResponseData } from "@thunderstore/thunderstore-api"; + +import "./Wiki.css"; interface WikiContentProps { page: PackageWikiPageResponseData; diff --git a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiFirstPage.tsx b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiFirstPage.tsx index a9137d47f..ba03090f3 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiFirstPage.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiFirstPage.tsx @@ -1,21 +1,22 @@ -import "./Wiki.css"; - -import { Await, type LoaderFunctionArgs } from "react-router"; -import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; -import { WikiContent } from "./WikiContent"; -import { isApiError } from "../../../../../../packages/thunderstore-api/src"; +import { Suspense, useMemo } from "react"; +import { Await, type LoaderFunctionArgs } from "react-router"; +import { useLoaderData } from "react-router"; + +import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; import { getPackagePermissions, getPackageWiki, getPackageWikiPage, -} from "@thunderstore/dapper-ts/src/methods/package"; -import { SkeletonBox } from "@thunderstore/cyberstorm"; -import { useMemo, Suspense } from "react"; +} from "@thunderstore/dapper-ts"; +import { isApiError } from "@thunderstore/thunderstore-api"; + +import "./Wiki.css"; +import { WikiContent } from "./WikiContent"; type ResultType = { wiki: Awaited> | undefined; diff --git a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiNewPage.tsx b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiNewPage.tsx index 8f611d8da..32ea46589 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiNewPage.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiNewPage.tsx @@ -1,11 +1,14 @@ -import "./Wiki.css"; - +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { useReducer, useState } from "react"; import { type LoaderFunctionArgs, useNavigate, useOutletContext, } from "react-router"; import { useLoaderData } from "react-router"; +import { Markdown } from "~/commonComponents/Markdown/Markdown"; +import { type OutletContextShape } from "~/root"; + import { Heading, NewButton, @@ -13,15 +16,13 @@ import { Tabs, useToast, } from "@thunderstore/cyberstorm"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; -import { useReducer, useState } from "react"; +import { classnames } from "@thunderstore/cyberstorm"; import { type PackageWikiPageCreateRequestData, postPackageWikiPageCreate, } from "@thunderstore/thunderstore-api"; -import { type OutletContextShape } from "~/root"; -import { Markdown } from "~/commonComponents/Markdown/Markdown"; -import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; + +import "./Wiki.css"; export async function loader({ params }: LoaderFunctionArgs) { if (params.communityId && params.namespaceId && params.packageId) { diff --git a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPage.tsx b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPage.tsx index aa4cd6825..cfa2132cd 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPage.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPage.tsx @@ -1,21 +1,22 @@ -import "./Wiki.css"; - -import { Await, type LoaderFunctionArgs, useParams } from "react-router"; -import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; import { getPublicEnvVariables, getSessionTools, } from "cyberstorm/security/publicEnvVariables"; -import { WikiContent } from "./WikiContent"; +import { Suspense } from "react"; +import { Await, type LoaderFunctionArgs, useParams } from "react-router"; +import { useLoaderData } from "react-router"; + +import { SkeletonBox } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; import { + getPackagePermissions, getPackageWiki, getPackageWikiPage, - getPackagePermissions, -} from "@thunderstore/dapper-ts/src/methods/package"; -import { isApiError } from "../../../../../../packages/thunderstore-api/src"; -import { Suspense } from "react"; -import { SkeletonBox } from "@thunderstore/cyberstorm"; +} from "@thunderstore/dapper-ts"; +import { isApiError } from "@thunderstore/thunderstore-api"; + +import "./Wiki.css"; +import { WikiContent } from "./WikiContent"; type ResultType = { wiki: Awaited> | undefined; diff --git a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPageEdit.tsx b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPageEdit.tsx index 435ee3802..151c89501 100644 --- a/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPageEdit.tsx +++ b/apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPageEdit.tsx @@ -1,16 +1,18 @@ -import "./Wiki.css"; - +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { useReducer, useState } from "react"; import { type LoaderFunctionArgs, useNavigate, useOutletContext, } from "react-router"; import { useLoaderData } from "react-router"; -import { DapperTs } from "@thunderstore/dapper-ts"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; +import { Markdown } from "~/commonComponents/Markdown/Markdown"; +import { type OutletContextShape } from "~/root"; + import { Heading, Modal, @@ -20,20 +22,19 @@ import { Tabs, useToast, } from "@thunderstore/cyberstorm"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; -import { useReducer, useState } from "react"; +import { classnames } from "@thunderstore/cyberstorm"; +import { DapperTs } from "@thunderstore/dapper-ts"; import { - deletePackageWikiPage, type PackageWikiPageEditRequestData, type PackageWikiPageResponseData, - postPackageWikiPageEdit, type RequestConfig, + deletePackageWikiPage, + postPackageWikiPageEdit, } from "@thunderstore/thunderstore-api"; -import { type OutletContextShape } from "~/root"; -import { Markdown } from "~/commonComponents/Markdown/Markdown"; -import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; import { ApiAction } from "@thunderstore/ts-api-react-actions"; +import "./Wiki.css"; + export async function loader({ params }: LoaderFunctionArgs) { if ( params.communityId && diff --git a/apps/cyberstorm-remix/app/p/team/Team.tsx b/apps/cyberstorm-remix/app/p/team/Team.tsx index 0d7130aab..4fef9da6e 100644 --- a/apps/cyberstorm-remix/app/p/team/Team.tsx +++ b/apps/cyberstorm-remix/app/p/team/Team.tsx @@ -1,15 +1,17 @@ +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; import { useLoaderData, useOutletContext } from "react-router"; -import "./Team.css"; import { PackageSearch } from "~/commonComponents/PackageSearch/PackageSearch"; +import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; + import { DapperTs } from "@thunderstore/dapper-ts"; + import { PackageOrderOptions } from "../../commonComponents/PackageSearch/components/PackageOrder"; import { type OutletContextShape } from "../../root"; -import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; -import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; import type { Route } from "./+types/Team"; +import "./Team.css"; export async function loader({ params, request }: Route.LoaderArgs) { if (params.communityId && params.namespaceId) { diff --git a/apps/cyberstorm-remix/app/root.tsx b/apps/cyberstorm-remix/app/root.tsx index bbe5c637e..4d4de375e 100644 --- a/apps/cyberstorm-remix/app/root.tsx +++ b/apps/cyberstorm-remix/app/root.tsx @@ -1,5 +1,15 @@ import "./styles/index.css"; -import "@thunderstore/cyberstorm-theme"; + +// import { LinksFunction } from "@remix-run/react/dist/routeModules"; +import { Provider as RadixTooltip } from "@radix-ui/react-tooltip"; +import { withSentry } from "@sentry/remix"; +import { + getPublicEnvVariables, + getSessionTools, + type publicEnvVariablesType, +} from "cyberstorm/security/publicEnvVariables"; +import { LinkLibrary } from "cyberstorm/utils/LinkLibrary"; +import { type ReactNode, Suspense, memo, useEffect, useRef } from "react"; import { Await, Links, @@ -14,40 +24,32 @@ import { useLocation, useMatches, } from "react-router"; -// import { LinksFunction } from "@remix-run/react/dist/routeModules"; -import { Provider as RadixTooltip } from "@radix-ui/react-tooltip"; +import { useHydrated } from "remix-utils/use-hydrated"; -import { LinkLibrary } from "cyberstorm/utils/LinkLibrary"; import { AdContainer, - isRecord, LinkingProvider, NewBreadCrumbs, NewBreadCrumbsLink, + ToastProvider, + isRecord, } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { DapperTs } from "@thunderstore/dapper-ts"; import { type CurrentUser } from "@thunderstore/dapper/types"; - -import { withSentry } from "@sentry/remix"; -import { memo, type ReactNode, Suspense, useEffect, useRef } from "react"; -import { useHydrated } from "remix-utils/use-hydrated"; -import Toast from "@thunderstore/cyberstorm/src/newComponents/Toast"; -import { Footer } from "./commonComponents/Footer/Footer"; import { type RequestConfig } from "@thunderstore/thunderstore-api"; -import { NavigationWrapper } from "./commonComponents/Navigation/NavigationWrapper"; -import { NamespacedStorageManager } from "@thunderstore/ts-api-react"; import { + NamespacedStorageManager, + SESSION_STORAGE_KEY, + StorageManager, getSessionContext, getSessionStale, - SESSION_STORAGE_KEY, runSessionValidationCheck, -} from "@thunderstore/ts-api-react/src/SessionContext"; -import { - getPublicEnvVariables, - type publicEnvVariablesType, -} from "cyberstorm/security/publicEnvVariables"; -import { StorageManager } from "@thunderstore/ts-api-react/src/storage"; +} from "@thunderstore/ts-api-react"; + import type { Route } from "./+types/root"; +import { Footer } from "./commonComponents/Footer/Footer"; +import { NavigationWrapper } from "./commonComponents/Navigation/NavigationWrapper"; // REMIX TODO: https://remix.run/docs/en/main/route/links // export const links: LinksFunction = () => [{ rel: "stylesheet", href: styles }]; @@ -338,7 +340,7 @@ export function Layout({ children }: { children: React.ReactNode }) { )}

- + {shouldShowAds ? : null} - + @@ -578,12 +580,19 @@ const TooltipProvider = memo(function TooltipProvider({ function App() { const data = useLoaderData(); - const dapper = new DapperTs(() => { - return { - apiHost: data?.publicEnvVariables.VITE_API_URL, - sessionId: data?.config.sessionId, - }; - }); + const sessionTools = getSessionTools(); + const dapper = new DapperTs( + () => { + return { + apiHost: data?.publicEnvVariables.VITE_API_URL, + sessionId: data?.config.sessionId, + }; + }, + () => + sessionTools.clearInvalidSession( + data?.publicEnvVariables.VITE_COOKIE_DOMAIN + ) + ); return ( RequestConfig }) { const revalidator = useRevalidator(); const toast = useToast(); + const [open, setOpen] = useState(false); + async function createTeamRevalidate() { setSessionStale(new NamespacedStorageManager(SESSION_STORAGE_KEY), true); revalidator.revalidate(); @@ -168,6 +173,7 @@ function CreateTeamForm(props: { config: () => RequestConfig }) { onSubmitSuccess: (fi) => { createTeamRevalidate(); updateFormFieldState({ field: "name", value: "" }); + setOpen(false); toast.addToast({ csVariant: "success", children: `Team ${fi.name} created!`, @@ -183,7 +189,7 @@ function CreateTeamForm(props: { config: () => RequestConfig }) { }, }); - const [open, setOpen] = useState(false); + const teamNameFieldProps = strongForm.getFieldComponentProps("name"); return ( RequestConfig }) { } > Create Team - -
- Enter the name of the team you wish to create. Team names can contain - the characters a-z A-Z 0-9 _ and must not start or end with an _. -
-
- - - updateFormFieldState({ - field: "name", - value: v.target.value, - }) - } - placeholder={"MyCoolTeam"} - id="teamName" - /> -
-
- - { - strongForm.submit().then(() => setOpen(false)); - }} - > - Create - - +
+ +
+ Enter the name of the team you wish to create. Team names can + contain the characters a-z A-Z 0-9 _ and must not start or end with + an _. +
+
+ + + updateFormFieldState({ + field: "name", + value: v.target.value, + }) + } + placeholder={"MyCoolTeam"} + id="teamName" + {...teamNameFieldProps} + /> +
+
+ + + Create + + +
); } diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/MemberAddForm.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/MemberAddForm.tsx index 0d056d337..5e168eb0d 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/MemberAddForm.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/MemberAddForm.tsx @@ -1,23 +1,24 @@ import { faPlus } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useState, useReducer } from "react"; +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { useReducer, useState } from "react"; import { - useToast, Modal, NewButton, NewIcon, - NewTextInput, NewSelect, + NewTextInput, type SelectOption, + useToast, } from "@thunderstore/cyberstorm"; import { - teamAddMember, type RequestConfig, type TeamAddMemberRequestData, + teamAddMember, } from "@thunderstore/thunderstore-api"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { RequiredIndicator } from "~/commonComponents/RequiredIndicator/RequiredIndicator"; const roleOptions: SelectOption<"owner" | "member">[] = [ { value: "member", label: "Member" }, @@ -95,6 +96,8 @@ export function MemberAddForm(props: { }, }); + const usernameFieldProps = strongForm.getFieldComponentProps("username"); + return ( } > - -
- Enter the username of the user you wish to add to the team{" "} - {props.teamName}. -
-
-
- - { - setError(null); - updateFormFieldState({ - field: "username", - value: e.target.value, - }); - }} - rootClasses="add-member-form__username-input" - id="username" - /> - {error &&
{error}
} +
+ +
+ Enter the username of the user you wish to add to the team{" "} + + {props.teamName} + + .
-
- - { - updateFormFieldState({ field: "role", value: value }); - }} - id="role" - /> +
+
+ + { + setError(null); + updateFormFieldState({ + field: "username", + value: e.target.value, + }); + }} + rootClasses="add-member-form__username-input" + id="username" + {...usernameFieldProps} + /> + {error &&
{error}
} +
+
+ + { + updateFormFieldState({ field: "role", value: value }); + }} + id="role" + /> +
-
-
- - - Add member - - + + + + Add member + + +
); } diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/Members.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/Members.tsx index c3894d1b9..9d035c7a8 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/Members.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/Members.tsx @@ -1,3 +1,6 @@ +import { type OutletContextShape } from "app/root"; +import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders"; +import { isTeamOwner } from "cyberstorm/utils/permissions"; import { Suspense } from "react"; import { Await, @@ -6,12 +9,9 @@ import { useRevalidator, } from "react-router"; -import { type OutletContextShape } from "app/root"; -import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders"; -import { isTeamOwner } from "cyberstorm/utils/permissions"; import { MemberAddForm } from "./MemberAddForm"; -import { MembersTable } from "./MembersTable"; import "./Members.css"; +import { MembersTable } from "./MembersTable"; export const clientLoader = makeTeamSettingsTabLoader( async (dapper, teamName) => ({ diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/MembersTable.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/MembersTable.tsx index 3dc63f637..719052b62 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/MembersTable.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Members/MembersTable.tsx @@ -1,5 +1,7 @@ import { faTrashCan } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { type OutletContextShape } from "app/root"; +import { isTeamOwner } from "cyberstorm/utils/permissions"; import { useState } from "react"; import { useOutletContext } from "react-router"; @@ -11,21 +13,18 @@ import { NewLink, NewSelect, NewTable, - useToast, type SelectOption, + useToast, } from "@thunderstore/cyberstorm"; -import { TableSort } from "@thunderstore/cyberstorm/src/newComponents/Table/Table"; +import { NewTableSort as TableSort } from "@thunderstore/cyberstorm"; import { - teamEditMember, - teamRemoveMember, type RequestConfig, type TeamMember, + teamEditMember, + teamRemoveMember, } from "@thunderstore/thunderstore-api"; import { ApiAction } from "@thunderstore/ts-api-react-actions"; -import { type OutletContextShape } from "app/root"; -import { isTeamOwner } from "cyberstorm/utils/permissions"; - const teamMemberColumns = [ { value: "User", disableSort: false }, { value: "Role", disableSort: false }, diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Profile/Profile.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Profile/Profile.tsx index ee9ed4cc0..35458c392 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Profile/Profile.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Profile/Profile.tsx @@ -1,3 +1,6 @@ +import { type OutletContextShape } from "app/root"; +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders"; import { Suspense, useReducer } from "react"; import { Await, @@ -8,14 +11,12 @@ import { import { NewButton, NewTextInput, useToast } from "@thunderstore/cyberstorm"; import { - teamDetailsEdit, type TeamDetails, type TeamDetailsEditRequestData, + teamDetailsEdit, } from "@thunderstore/thunderstore-api"; -import { type OutletContextShape } from "app/root"; -import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { isTeamOwner } from "cyberstorm/utils/permissions"; import "./Profile.css"; export const clientLoader = makeTeamSettingsTabLoader( @@ -49,6 +50,8 @@ function ProfileForm(props: { team: TeamDetails }) { const revalidator = useRevalidator(); const toast = useToast(); + const formDisabled = !isTeamOwner(team.name, outletContext.currentUser); + function formFieldUpdateAction( state: TeamDetailsEditRequestData, action: { @@ -90,6 +93,11 @@ function ProfileForm(props: { team: TeamDetails }) { InputErrors >({ inputs: formInputs, + validators: { + donation_link: { + url: true, + }, + }, refiner: async (inputs: typeof formInputs) => ({ donation_link: nullForEmptyString(inputs.donation_link), }), @@ -111,6 +119,11 @@ function ProfileForm(props: { team: TeamDetails }) { }, }); + const donationLinkFieldProps = strongForm.getFieldComponentProps( + "donation_link", + { disabled: formDisabled } + ); + return (
@@ -131,10 +144,16 @@ function ProfileForm(props: { team: TeamDetails }) { }) } rootClasses="team-profile__input" + disabled={formDisabled} + {...donationLinkFieldProps} />
- + Save changes
diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccountRemoveModal.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccountRemoveModal.tsx index 465355edb..8eec0d1bf 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccountRemoveModal.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccountRemoveModal.tsx @@ -1,10 +1,12 @@ import { faTrash } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { type OutletContextShape } from "app/root"; +import { isTeamOwner } from "cyberstorm/utils/permissions"; import { + Modal, NewAlert, NewButton, - Modal, NewIcon, useToast, } from "@thunderstore/cyberstorm"; @@ -14,8 +16,6 @@ import { } from "@thunderstore/thunderstore-api"; import { ApiAction } from "@thunderstore/ts-api-react-actions"; -import { type OutletContextShape } from "app/root"; -import { isTeamOwner } from "cyberstorm/utils/permissions"; import "./ServiceAccounts.css"; interface ServiceAccountRemoveModalProps { diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx index ab4fc1454..17a811dd9 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccounts.tsx @@ -1,5 +1,9 @@ import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { type OutletContextShape } from "app/root"; +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders"; +import { isTeamOwner } from "cyberstorm/utils/permissions"; import { Suspense, useReducer, useState } from "react"; import { Await, @@ -9,24 +13,21 @@ import { } from "react-router"; import { + CodeBox, + Modal, NewAlert, NewButton, - Modal, NewIcon, NewTextInput, - CodeBox, } from "@thunderstore/cyberstorm"; import { - teamAddServiceAccount, type TeamServiceAccountAddRequestData, + teamAddServiceAccount, } from "@thunderstore/thunderstore-api"; -import { type OutletContextShape } from "app/root"; -import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders"; -import { isTeamOwner } from "cyberstorm/utils/permissions"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; -import { ServiceAccountsTable } from "./ServiceAccountsTable"; +import { RequiredIndicator } from "~/commonComponents/RequiredIndicator/RequiredIndicator"; import "./ServiceAccounts.css"; +import { ServiceAccountsTable } from "./ServiceAccountsTable"; export const clientLoader = makeTeamSettingsTabLoader( async (dapper, teamName) => ({ @@ -154,6 +155,8 @@ function AddServiceAccountForm(props: { }, }); + const nicknameFieldProps = strongForm.getFieldComponentProps("nickname"); + const handleOpenChange = (nextOpen: boolean) => { setOpen(nextOpen); if (!nextOpen) { @@ -197,46 +200,46 @@ function AddServiceAccountForm(props: { ) : ( - + <>
{ - e.preventDefault(); - if (strongForm.isReady) { - strongForm.submit(); - } - }} + onSubmit={strongForm.handleSubmit} > -
- Enter the nickname of the service account you wish to add to the - team {props.teamName} -
-
- { - updateFormFieldState({ - field: "nickname", - value: e.target.value, - }); - }} - placeholder={"ExampleName"} - maxLength={32} - /> -
- Max. 32 characters + +
+ Enter the nickname of the service account you wish to add to the + team {props.teamName}
-
- {error && {error}} + +
+ { + updateFormFieldState({ + field: "nickname", + value: e.target.value, + }); + }} + placeholder={"ExampleName"} + maxLength={32} + {...nicknameFieldProps} + /> +
+ Max. 32 characters +
+
+ {error && {error}} + + + + Add Service Account + + - - )} - {serviceAccountAdded ? null : ( - - - Add Service Account - - + )} ); diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccountsTable.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccountsTable.tsx index 61a9481df..723f5880e 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccountsTable.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/ServiceAccounts/ServiceAccountsTable.tsx @@ -1,10 +1,10 @@ +import { type OutletContextShape } from "app/root"; import { useOutletContext } from "react-router"; -import { NewTable, Heading } from "@thunderstore/cyberstorm"; -import { TableSort } from "@thunderstore/cyberstorm/src/newComponents/Table/Table"; +import { Heading, NewTable } from "@thunderstore/cyberstorm"; +import { NewTableSort as TableSort } from "@thunderstore/cyberstorm"; import { type TeamServiceAccount } from "@thunderstore/thunderstore-api"; -import { type OutletContextShape } from "app/root"; import { ServiceAccountRemoveModal } from "./ServiceAccountRemoveModal"; import "./ServiceAccounts.css"; diff --git a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Settings/Settings.tsx b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Settings/Settings.tsx index 563625f48..9ddd8d3d2 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/tabs/Settings/Settings.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/tabs/Settings/Settings.tsx @@ -1,5 +1,9 @@ import { faTrashCan } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { type OutletContextShape } from "app/root"; +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders"; +import { isTeamOwner } from "cyberstorm/utils/permissions"; import { Suspense, useReducer, useState } from "react"; import { Await, @@ -8,10 +12,9 @@ import { useOutletContext, } from "react-router"; -import "./Settings.css"; import { - NewAlert, Modal, + NewAlert, NewButton, NewIcon, NewTextInput, @@ -19,16 +22,13 @@ import { } from "@thunderstore/cyberstorm"; import { type RequestConfig, - teamDisband, type TeamDisbandRequestData, + teamDisband, teamRemoveMember, } from "@thunderstore/thunderstore-api"; import { ApiAction } from "@thunderstore/ts-api-react-actions"; -import { type OutletContextShape } from "app/root"; -import { makeTeamSettingsTabLoader } from "cyberstorm/utils/dapperClientLoaders"; -import { isTeamOwner } from "cyberstorm/utils/permissions"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import "./Settings.css"; export const clientLoader = makeTeamSettingsTabLoader( async (dapper, teamName) => ({ diff --git a/apps/cyberstorm-remix/app/settings/teams/team/teamSettings.tsx b/apps/cyberstorm-remix/app/settings/teams/team/teamSettings.tsx index af3894797..1ecd81496 100644 --- a/apps/cyberstorm-remix/app/settings/teams/team/teamSettings.tsx +++ b/apps/cyberstorm-remix/app/settings/teams/team/teamSettings.tsx @@ -1,15 +1,15 @@ +import { PageHeader } from "app/commonComponents/PageHeader/PageHeader"; +import { type OutletContextShape } from "app/root"; import { + type MetaFunction, Outlet, useLocation, useOutletContext, useParams, - type MetaFunction, } from "react-router"; import { NewLink, Tabs } from "@thunderstore/cyberstorm"; -import { PageHeader } from "app/commonComponents/PageHeader/PageHeader"; -import { type OutletContextShape } from "app/root"; import "./teamSettings.css"; export const meta: MetaFunction = ({ params }) => { diff --git a/apps/cyberstorm-remix/app/settings/user/Account/Account.tsx b/apps/cyberstorm-remix/app/settings/user/Account/Account.tsx index 56dbb0e2e..1dfa927e7 100644 --- a/apps/cyberstorm-remix/app/settings/user/Account/Account.tsx +++ b/apps/cyberstorm-remix/app/settings/user/Account/Account.tsx @@ -1,5 +1,13 @@ -import { useOutletContext, useRevalidator, useNavigate } from "react-router"; -import "./Account.css"; +import { faTrashCan } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { useReducer } from "react"; +import { useNavigate, useOutletContext, useRevalidator } from "react-router"; +import { useHydrated } from "remix-utils/use-hydrated"; +import { Loading } from "~/commonComponents/Loading/Loading"; +import { NotLoggedIn } from "~/commonComponents/NotLoggedIn/NotLoggedIn"; +import { type OutletContextShape } from "~/root"; + import { NewAlert, NewButton, @@ -7,15 +15,9 @@ import { NewTextInput, useToast, } from "@thunderstore/cyberstorm"; -import { faTrashCan } from "@fortawesome/pro-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { NotLoggedIn } from "~/commonComponents/NotLoggedIn/NotLoggedIn"; -import { Loading } from "~/commonComponents/Loading/Loading"; -import { type OutletContextShape } from "~/root"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; -import { useReducer } from "react"; import { userDelete } from "@thunderstore/thunderstore-api"; -import { useHydrated } from "remix-utils/use-hydrated"; + +import "./Account.css"; export default function Account() { const outletContext = useOutletContext() as OutletContextShape; diff --git a/apps/cyberstorm-remix/app/settings/user/Connections/Connections.tsx b/apps/cyberstorm-remix/app/settings/user/Connections/Connections.tsx index 9a140febe..090d595d8 100644 --- a/apps/cyberstorm-remix/app/settings/user/Connections/Connections.tsx +++ b/apps/cyberstorm-remix/app/settings/user/Connections/Connections.tsx @@ -1,24 +1,19 @@ -import { type ReactElement, useRef } from "react"; - +import { faDiscord, faGithub } from "@fortawesome/free-brands-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faGithub, faDiscord } from "@fortawesome/free-brands-svg-icons"; - -import { useOutletContext, useRevalidator } from "react-router"; - -import { NewLink, OverwolfLogo, useToast } from "@thunderstore/cyberstorm"; -import { ApiAction } from "@thunderstore/ts-api-react-actions"; -import { ApiError } from "@thunderstore/thunderstore-api"; - -import { buildAuthLoginUrl } from "cyberstorm/utils/ThunderstoreAuth"; import { getPublicEnvVariables } from "cyberstorm/security/publicEnvVariables"; - +import { buildAuthLoginUrl } from "cyberstorm/utils/ThunderstoreAuth"; +import { type ReactElement, useRef } from "react"; +import { useOutletContext, useRevalidator } from "react-router"; +import { useHydrated } from "remix-utils/use-hydrated"; import { Connection } from "~/commonComponents/Connection/Connection"; -import { NotLoggedIn } from "~/commonComponents/NotLoggedIn/NotLoggedIn"; import { Loading } from "~/commonComponents/Loading/Loading"; +import { NotLoggedIn } from "~/commonComponents/NotLoggedIn/NotLoggedIn"; import { type OutletContextShape } from "~/root"; -import { userLinkedAccountDisconnect } from "../../../../../../packages/thunderstore-api/src"; -import { useHydrated } from "remix-utils/use-hydrated"; +import { NewLink, OverwolfLogo, useToast } from "@thunderstore/cyberstorm"; +import { ApiError } from "@thunderstore/thunderstore-api"; +import { userLinkedAccountDisconnect } from "@thunderstore/thunderstore-api"; +import { ApiAction } from "@thunderstore/ts-api-react-actions"; type ProvidersType = { name: string; diff --git a/apps/cyberstorm-remix/app/settings/user/Settings.tsx b/apps/cyberstorm-remix/app/settings/user/Settings.tsx index 54ef6ac9e..da2f4e8cb 100644 --- a/apps/cyberstorm-remix/app/settings/user/Settings.tsx +++ b/apps/cyberstorm-remix/app/settings/user/Settings.tsx @@ -1,8 +1,10 @@ -import "./Settings.css"; import { Outlet, useLocation, useOutletContext } from "react-router"; -import { NewLink, Tabs } from "@thunderstore/cyberstorm"; import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; + +import { NewLink, Tabs } from "@thunderstore/cyberstorm"; + import { type OutletContextShape } from "../../root"; +import "./Settings.css"; export default function UserSettings() { const context = useOutletContext(); diff --git a/apps/cyberstorm-remix/app/tools/manifest-validator/manifestValidator.tsx b/apps/cyberstorm-remix/app/tools/manifest-validator/manifestValidator.tsx index 4e8bfd72e..31d198eb3 100644 --- a/apps/cyberstorm-remix/app/tools/manifest-validator/manifestValidator.tsx +++ b/apps/cyberstorm-remix/app/tools/manifest-validator/manifestValidator.tsx @@ -1,3 +1,9 @@ +import { Buffer } from "buffer"; +import { useEffect, useState } from "react"; +import { useOutletContext } from "react-router"; +import { useDebounce } from "use-debounce"; +import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; + import { CodeInput, NewAlert, @@ -5,19 +11,14 @@ import { isRecord, isStringArray, } from "@thunderstore/cyberstorm"; -import "./manifestValidator.css"; -import { useEffect, useState } from "react"; -import { useOutletContext } from "react-router"; -import { Buffer } from "buffer"; import { - isApiError, type RequestConfig, + isApiError, toolsManifestValidate, } from "@thunderstore/thunderstore-api"; -import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; -import { type OutletContextShape } from "../../root"; -import { useDebounce } from "use-debounce"; +import { type OutletContextShape } from "../../root"; +import "./manifestValidator.css"; export default function ManifestValidator() { const outletContext = useOutletContext() as OutletContextShape; @@ -28,10 +29,7 @@ export default function ManifestValidator() { const selectOptions = currentUser ? currentUser.teams.map((team) => { - if (typeof team === "string") { - return { value: team, label: team }; - } - return { value: team.name, label: team.name }; + return { value: team, label: team }; }) : []; diff --git a/apps/cyberstorm-remix/app/tools/markdown-preview/markdownPreview.tsx b/apps/cyberstorm-remix/app/tools/markdown-preview/markdownPreview.tsx index 1aad03509..ec9130242 100644 --- a/apps/cyberstorm-remix/app/tools/markdown-preview/markdownPreview.tsx +++ b/apps/cyberstorm-remix/app/tools/markdown-preview/markdownPreview.tsx @@ -1,17 +1,18 @@ +import { useEffect, useState } from "react"; import { useOutletContext } from "react-router"; -import "./MarkdownPreview.css"; +import { useDebounce } from "use-debounce"; +import { Markdown } from "~/commonComponents/Markdown/Markdown"; + import { CodeInput, isRecord } from "@thunderstore/cyberstorm"; import { - isApiError, type RequestConfig, + isApiError, toolsMarkdownPreview, } from "@thunderstore/thunderstore-api"; -import { useState, useEffect } from "react"; + import { PageHeader } from "../../commonComponents/PageHeader/PageHeader"; import { type OutletContextShape } from "../../root"; - -import { useDebounce } from "use-debounce"; -import { Markdown } from "~/commonComponents/Markdown/Markdown"; +import "./MarkdownPreview.css"; export default function MarkdownPreview() { const outletContext = useOutletContext() as OutletContextShape; diff --git a/apps/cyberstorm-remix/app/tools/package-format-docs/packageFormatDocs.tsx b/apps/cyberstorm-remix/app/tools/package-format-docs/packageFormatDocs.tsx index 886f5e3bc..1027a1c83 100644 --- a/apps/cyberstorm-remix/app/tools/package-format-docs/packageFormatDocs.tsx +++ b/apps/cyberstorm-remix/app/tools/package-format-docs/packageFormatDocs.tsx @@ -1,9 +1,11 @@ -import "./PackageFormatDocs.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheck, faXmark } from "@fortawesome/free-solid-svg-icons"; -import { CodeBox, NewIcon, NewTable } from "@thunderstore/cyberstorm"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; +import { CodeBox, NewIcon, NewTable } from "@thunderstore/cyberstorm"; + +import "./PackageFormatDocs.css"; + export default function PackageFormatDocs() { return ( <> diff --git a/apps/cyberstorm-remix/app/upload/upload.tsx b/apps/cyberstorm-remix/app/upload/upload.tsx index 2035bcc7b..a5f856cd5 100644 --- a/apps/cyberstorm-remix/app/upload/upload.tsx +++ b/apps/cyberstorm-remix/app/upload/upload.tsx @@ -1,48 +1,52 @@ -import "./Upload.css"; +import { faCheckCircle } from "@fortawesome/free-solid-svg-icons"; +import { + faArrowUpRight, + faFileZip, + faTreasureChest, + faUsers, +} from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + getPublicEnvVariables, + getSessionTools, +} from "cyberstorm/security/publicEnvVariables"; +import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { useCallback, useEffect, useReducer, useRef, useState } from "react"; +import { type MetaFunction } from "react-router"; +import { useLoaderData, useOutletContext } from "react-router"; + import { + Heading, NewButton, NewIcon, + NewLink, NewSelectSearch, NewSwitch, - Heading, - NewLink, NewTable, NewTableSort, NewTag, useToast, } from "@thunderstore/cyberstorm"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { PageHeader } from "../commonComponents/PageHeader/PageHeader"; -import { DnDFileInput } from "@thunderstore/react-dnd"; -import { useCallback, useEffect, useReducer, useRef, useState } from "react"; +import { classnames } from "@thunderstore/cyberstorm"; import { - MultipartUpload, - type IBaseUploadHandle, -} from "@thunderstore/ts-uploader"; -import { - faFileZip, - faTreasureChest, - faUsers, - faArrowUpRight, -} from "@fortawesome/pro-solid-svg-icons"; -import { type UserMedia } from "@thunderstore/ts-uploader/src/uploaders/types"; -import { DapperTs } from "@thunderstore/dapper-ts"; -import { type MetaFunction } from "react-router"; -import { useLoaderData, useOutletContext } from "react-router"; + DapperTs, + postPackageSubmissionMetadata, +} from "@thunderstore/dapper-ts"; import { type PackageSubmissionResult, type PackageSubmissionStatus, } from "@thunderstore/dapper/types"; +import { DnDFileInput } from "@thunderstore/react-dnd"; import { type PackageSubmissionRequestData } from "@thunderstore/thunderstore-api"; -import { type OutletContextShape } from "../root"; -import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; -import { postPackageSubmissionMetadata } from "@thunderstore/dapper-ts/src/methods/package"; -import { faCheckCircle } from "@fortawesome/free-solid-svg-icons"; -import { classnames } from "@thunderstore/cyberstorm/src/utils/utils"; import { - getPublicEnvVariables, - getSessionTools, -} from "cyberstorm/security/publicEnvVariables"; + type IBaseUploadHandle, + MultipartUpload, +} from "@thunderstore/ts-uploader"; +import { type UserMedia } from "@thunderstore/ts-uploader"; + +import { PageHeader } from "../commonComponents/PageHeader/PageHeader"; +import { type OutletContextShape } from "../root"; +import "./Upload.css"; interface CommunityOption { value: string; diff --git a/apps/cyberstorm-remix/cyberstorm/security/publicEnvVariables.ts b/apps/cyberstorm-remix/cyberstorm/security/publicEnvVariables.ts index a0358d0c4..85675ba4c 100644 --- a/apps/cyberstorm-remix/cyberstorm/security/publicEnvVariables.ts +++ b/apps/cyberstorm-remix/cyberstorm/security/publicEnvVariables.ts @@ -1,4 +1,5 @@ -import { getSessionContext } from "@thunderstore/ts-api-react/src/SessionContext"; +import { getSessionContext } from "@thunderstore/ts-api-react"; + import { isRecord } from "../utils/typeChecks"; export type publicEnvVariablesKeys = diff --git a/apps/cyberstorm-remix/cyberstorm/session/__tests__/SessionContext.test.ts b/apps/cyberstorm-remix/cyberstorm/session/__tests__/SessionContext.test.ts index 2444c237e..d1ec366d0 100644 --- a/apps/cyberstorm-remix/cyberstorm/session/__tests__/SessionContext.test.ts +++ b/apps/cyberstorm-remix/cyberstorm/session/__tests__/SessionContext.test.ts @@ -1,31 +1,141 @@ -import { assert, describe, it, beforeEach } from "vitest"; import { - SESSION_STORAGE_KEY, - CURRENT_USER_KEY, - STALE_KEY, + afterEach, + assert, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; + +import type { User } from "@thunderstore/thunderstore-api"; +import { API_HOST_KEY, COOKIE_DOMAIN_KEY, + CURRENT_USER_KEY, + SESSION_STORAGE_KEY, + STALE_KEY, + StorageManager, + clearCookies, + clearInvalidSession, + clearSession, + getConfig, + getCookie, getSessionContext, + getSessionCurrentUser, getSessionStale, - setSessionStale, runSessionValidationCheck, + setSessionStale, storeCurrentUser, - clearSession, - getSessionCurrentUser, -} from "@thunderstore/ts-api-react/src/SessionContext"; -import { StorageManager } from "@thunderstore/ts-api-react/src/storage"; -import type { User } from "@thunderstore/thunderstore-api"; + updateCurrentUser, +} from "@thunderstore/ts-api-react"; + +vi.mock("@thunderstore/dapper-ts", () => ({ + DapperTs: vi.fn(), +})); + +let getCurrentUserMock: ReturnType; describe("SessionContext", () => { const testApiHost = "https://api.example.invalid"; const testCookieDomain = ".example.invalid"; - beforeEach(() => { + beforeEach(async () => { // Clear localStorage before each test window.localStorage.clear(); // Clear cookies document.cookie = "sessionid=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; + + getCurrentUserMock = vi.fn().mockResolvedValue(null); + const { DapperTs } = await import("@thunderstore/dapper-ts"); + vi.mocked(DapperTs).mockImplementation( + () => ({ getCurrentUser: getCurrentUserMock }) as any + ); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe("getCookie", () => { + it("should return cookie value when present", () => { + document.cookie = "sessionid=test-session-id"; + assert.strictEqual(getCookie("sessionid"), "test-session-id"); + }); + + it("should return null when cookie is missing", () => { + document.cookie = "other=123"; + assert.isNull(getCookie("sessionid")); + }); + }); + + describe("getConfig", () => { + it("should use domain fallback when apiHost is not stored", () => { + document.cookie = "sessionid=abc"; + const storage = new StorageManager(SESSION_STORAGE_KEY); + + const config = getConfig(storage, "http://fallback.invalid"); + assert.strictEqual(config.apiHost, "http://fallback.invalid"); + assert.strictEqual(config.sessionId, "abc"); + }); + + it("should use stored apiHost over domain", () => { + const storage = new StorageManager(SESSION_STORAGE_KEY); + storage.setValue(API_HOST_KEY, "http://stored.invalid"); + + const config = getConfig(storage, "http://fallback.invalid"); + assert.strictEqual(config.apiHost, "http://stored.invalid"); + }); + }); + + describe("clearCookies", () => { + it("should clear sessionid cookie", () => { + document.cookie = "sessionid=abc; path=/"; + clearCookies("localhost"); + assert.isNull(getCookie("sessionid")); + }); + }); + + describe("clearInvalidSession", () => { + it("should clear current user and mark session stale", () => { + const storage = new StorageManager(SESSION_STORAGE_KEY); + storage.setValue(COOKIE_DOMAIN_KEY, ".example.invalid"); + + const testUser: User = { + username: "testUser", + capabilities: [], + connections: [], + subscription: { expires: null }, + teams: [], + teams_full: [], + }; + storeCurrentUser(storage, testUser); + + clearInvalidSession(storage); + + assert.isNull(storage.safeGetJsonValue(CURRENT_USER_KEY)); + assert.strictEqual(storage.safeGetValue(STALE_KEY), "yes"); + }); + + it("should not throw if storage operations fail", () => { + const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + + const brokenStorage = { + safeGetValue: () => { + throw new Error("boom"); + }, + removeValue: () => { + throw new Error("boom"); + }, + setValue: () => { + throw new Error("boom"); + }, + } as unknown as StorageManager; + + clearInvalidSession(brokenStorage); + assert.isTrue(errorSpy.mock.calls.length >= 1); + }); }); describe("StorageManager", () => { @@ -303,5 +413,47 @@ describe("SessionContext", () => { assert.deepEqual(result.capabilities, ["cap1"]); assert.deepEqual(result.teams, ["team1"]); }); + + it("should throw when stored user is invalid", async () => { + const storage = new StorageManager(SESSION_STORAGE_KEY); + storage.setJsonValue(CURRENT_USER_KEY, { username: 123 } as unknown); + setSessionStale(storage, false); + + await expect(getSessionCurrentUser(storage, false)).rejects.toThrow( + /Failed to parse current user/ + ); + }); + }); + + describe("updateCurrentUser", () => { + it("should store currentUser and clear stale when user is returned", async () => { + const storage = new StorageManager(SESSION_STORAGE_KEY); + + getCurrentUserMock.mockResolvedValue({ + username: "testUser", + capabilities: [], + connections: [], + subscription: { expires: null }, + teams: [], + teams_full: [], + }); + + await updateCurrentUser(storage); + + const storedUser = storage.safeGetJsonValue(CURRENT_USER_KEY) as User; + assert.strictEqual(storedUser.username, "testUser"); + assert.strictEqual(storage.safeGetValue(STALE_KEY), "no"); + }); + + it("should clear currentUser when API returns null/empty and clear stale", async () => { + const storage = new StorageManager(SESSION_STORAGE_KEY); + storage.setJsonValue(CURRENT_USER_KEY, { username: "old" } as unknown); + + getCurrentUserMock.mockResolvedValue(null); + await updateCurrentUser(storage); + + assert.isNull(storage.safeGetJsonValue(CURRENT_USER_KEY)); + assert.strictEqual(storage.safeGetValue(STALE_KEY), "no"); + }); }); }); diff --git a/apps/cyberstorm-remix/cyberstorm/utils/LinkLibrary.tsx b/apps/cyberstorm-remix/cyberstorm/utils/LinkLibrary.tsx index dd4270c56..5618ab80f 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/LinkLibrary.tsx +++ b/apps/cyberstorm-remix/cyberstorm/utils/LinkLibrary.tsx @@ -1,10 +1,11 @@ +import { type PropsWithChildren } from "react"; +import React from "react"; +import { Link as RRLink, type LinkProps as RRLinkProps } from "react-router"; + import type { LinkLibrary, ThunderstoreLinkProps, } from "@thunderstore/cyberstorm"; -import { type LinkProps as RRLinkProps, Link as RRLink } from "react-router"; -import { type PropsWithChildren } from "react"; -import React from "react"; interface LinkProps extends React.AnchorHTMLAttributes, diff --git a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/useStrongForm.test.ts b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/useStrongForm.test.ts new file mode 100644 index 000000000..fe3c62aad --- /dev/null +++ b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/useStrongForm.test.ts @@ -0,0 +1,1176 @@ +import React, { useLayoutEffect, useRef } from "react"; +import { createRoot } from "react-dom/client"; +import { act } from "react"; +import { afterEach, assert, describe, expect, it, vi } from "vitest"; + +import { + ParseError, + RequestBodyParseError, + RequestQueryParamsParseError, +} from "../../../../../../packages/thunderstore-api/src"; +import { useStrongForm } from "../useStrongForm"; +import type { Validator } from "../validation"; + +function deferred() { + let resolve!: (value: T) => void; + let reject!: (reason?: unknown) => void; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +} + +async function flushMicrotasks(times = 3) { + for (let i = 0; i < times; i++) { + // eslint-disable-next-line no-await-in-loop + await Promise.resolve(); + } +} + +function render(element: React.ReactElement) { + const container = document.createElement("div"); + document.body.appendChild(container); + const root = createRoot(container); + + let didUnmount = false; + const unmount = () => { + if (didUnmount) return; + didUnmount = true; + act(() => { + root.unmount(); + }); + container.remove(); + }; + + cleanupMounted.push(unmount); + + act(() => { + root.render(element); + }); + + return { + root, + container, + unmount, + }; +} + +type Inputs = { name: string }; +type AnyStrongForm = ReturnType< + typeof useStrongForm +>; + +// React 18 act() warning suppression: mark the environment as act-capable. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +function useConst(create: () => T): T { + const ref = useRef(null); + if (ref.current === null) { + ref.current = create(); + } + return ref.current; +} + +function assertAssigned(value: T | null | undefined): asserts value is T { + if (value == null) { + throw new Error("Expected value to be assigned"); + } +} + +const cleanupMounted: Array<() => void> = []; + +afterEach(() => { + while (cleanupMounted.length > 0) { + cleanupMounted.pop()?.(); + } +}); + +describe("StrongForm.useStrongForm", () => { + it("provides required/aria-required and invalid state based on interactions", async () => { + let strongForm: AnyStrongForm | undefined; + + const submitor = vi.fn(async () => "ok"); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + string, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "" })), + validators: useConst(() => ({ name: { required: true } }) as const), + submitor, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + const initialProps = strongForm.getFieldComponentProps("name"); + assert.equal(initialProps.required, true); + assert.equal(initialProps["aria-required"], true); + assert.equal(initialProps["aria-invalid"], false); + + await act(async () => { + strongForm!.getFieldInteractionProps("name").onFocus(); + strongForm!.getFieldInteractionProps("name").onFocus(); + strongForm!.getFieldInteractionProps("name").onBlur(); + strongForm!.getFieldInteractionProps("name").onBlur(); + await flushMicrotasks(); + }); + + const afterInteractionProps = strongForm.getFieldComponentProps("name"); + assert.equal(afterInteractionProps["aria-invalid"], true); + assert.ok(afterInteractionProps.csModifiers?.includes("invalid")); + + const disabledProps = strongForm.getFieldComponentProps("name", { + disabled: true, + }); + assert.ok(disabledProps.csModifiers?.includes("disabled")); + + unmount(); + }); + + it("handleSubmit prevents enter-submit when required fields are trim-empty", async () => { + let strongForm: AnyStrongForm | undefined; + + const submitor = vi.fn(async () => undefined); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: " " })), + validators: useConst(() => ({ name: { required: true } }) as const), + submitor, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + assert.equal(strongForm.isReady, false); + + const preventDefault = vi.fn(); + await act(async () => { + strongForm!.handleSubmit({ preventDefault }); + await flushMicrotasks(); + }); + + assert.equal(preventDefault.mock.calls.length, 1); + assert.equal(submitor.mock.calls.length, 0); + + // submit attempt should make the invalid UI state visible + assert.equal(strongForm.getFieldState("name").isInvalid, true); + + unmount(); + }); + + it("submit returns early when !isReady (without calling submitor)", async () => { + let strongForm: AnyStrongForm | undefined; + + const submitor = vi.fn(async () => "ok"); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + string, + Error, + unknown + >({ + inputs: useConst(() => ({ name: " " })), + validators: useConst(() => ({ name: { required: true } }) as const), + submitor, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + assert.equal(strongForm.isReady, false); + + await act(async () => { + await strongForm!.submit(); + await flushMicrotasks(); + }); + + assert.equal(submitor.mock.calls.length, 0); + + unmount(); + }); + + it("clears submitOutput + submitError when inputs change", async () => { + let strongForm: AnyStrongForm | undefined; + let setInputs: React.Dispatch> | undefined; + + const submitor = vi.fn(async () => "ok"); + + const { unmount } = render( + React.createElement(function Harness() { + const [inputs, innerSetInputs] = React.useState({ name: "ok" }); + setInputs = innerSetInputs; + + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + string, + Error, + unknown + >({ + inputs, + validators: useConst(() => ({ name: { required: true } }) as const), + submitor, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + assertAssigned(setInputs); + + await act(async () => { + await strongForm!.submit(); + await flushMicrotasks(); + }); + + assert.equal(strongForm.submitOutput, "ok"); + assert.equal(strongForm.submitError, undefined); + + await act(async () => { + setInputs!({ name: "changed" }); + await flushMicrotasks(); + }); + + assert.equal(strongForm.submitOutput, undefined); + assert.equal(strongForm.submitError, undefined); + + unmount(); + }); + + it("does not restart refinement when inputs change while refining", async () => { + let strongForm: AnyStrongForm | undefined; + let setInputs: React.Dispatch> | undefined; + + const refinerDeferred = deferred(); + const refiner = vi.fn(async () => refinerDeferred.promise); + + const { unmount } = render( + React.createElement(function Harness() { + const [inputs, innerSetInputs] = React.useState({ name: "ok" }); + setInputs = innerSetInputs; + + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs, + refiner, + submitor: async () => undefined, + }); + + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + assertAssigned(setInputs); + assert.equal(strongForm.refining, true); + assert.equal(refiner.mock.calls.length, 1); + + await act(async () => { + setInputs!({ name: "changed" }); + await flushMicrotasks(); + }); + + // The effect is keyed on inputs, but should no-op while refining. + assert.equal(refiner.mock.calls.length, 1); + + refinerDeferred.resolve({ name: "changed" }); + await act(async () => { + await flushMicrotasks(); + }); + + unmount(); + }); + + it("throws 'Form has not been refined yet!' if submit happens before first effect", async () => { + const onSubmitError = vi.fn(); + const submitErrorDeferred = deferred(); + + render( + React.createElement(function Harness() { + const strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + refiner: async (inputs) => inputs, + submitor: async () => undefined, + onSubmitError, + }); + + useLayoutEffect(() => { + void strongForm.submit().then( + () => submitErrorDeferred.resolve("no-error"), + (err) => submitErrorDeferred.resolve(err) + ); + }, []); + + return React.createElement("div"); + }) + ); + + const result = await submitErrorDeferred.promise; + assert.instanceOf(result, Error); + assert.match((result as Error).message, /not been refined yet/i); + assert.equal(onSubmitError.mock.calls.length, 1); + }); + + it("throws while refining", async () => { + let strongForm: AnyStrongForm | undefined; + + const refinerDeferred = deferred(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + refiner: async () => refinerDeferred.promise, + submitor: async () => undefined, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await expect(strongForm.submit()).rejects.toThrow(/still refining/i); + + refinerDeferred.resolve({ name: "ok" }); + + unmount(); + }); + + it("calls onSubmitError when submit is invoked while refining", async () => { + let strongForm: AnyStrongForm | undefined; + + const refinerDeferred = deferred(); + const onSubmitError = vi.fn(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + refiner: async () => refinerDeferred.promise, + submitor: async () => undefined, + onSubmitError, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await expect(strongForm.submit()).rejects.toThrow(/still refining/i); + assert.equal(onSubmitError.mock.calls.length, 1); + assert.match( + String(onSubmitError.mock.calls[0]?.[0]?.message), + /still refining/i + ); + + refinerDeferred.resolve({ name: "ok" }); + unmount(); + }); + + it("throws refineError and calls onSubmitError with a generic refinement error", async () => { + let strongForm: AnyStrongForm | undefined; + + const refineFailure = new Error("refine failed"); + const onSubmitError = vi.fn(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + refiner: async () => { + throw refineFailure; + }, + submitor: async () => undefined, + onSubmitError, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await expect(strongForm.submit()).rejects.toBe(refineFailure); + + assert.equal(onSubmitError.mock.calls.length, 1); + assert.match( + String(onSubmitError.mock.calls[0]?.[0]?.message), + /refinement failed/i + ); + + unmount(); + }); + + it("throws when submitting twice", async () => { + let strongForm: AnyStrongForm | undefined; + + const submitorDeferred = deferred(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => submitorDeferred.promise, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + let firstSubmitPromise: Promise; + await act(async () => { + firstSubmitPromise = strongForm!.submit(); + await flushMicrotasks(); + }); + + await expect(strongForm.submit()).rejects.toThrow(/already submitting/i); + + submitorDeferred.resolve(undefined); + await firstSubmitPromise!; + + unmount(); + }); + + it("calls onSubmitError when submitting twice", async () => { + let strongForm: AnyStrongForm | undefined; + + const submitorDeferred = deferred(); + const onSubmitError = vi.fn(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => submitorDeferred.promise, + onSubmitError, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + let firstSubmitPromise: Promise; + await act(async () => { + firstSubmitPromise = strongForm!.submit(); + await flushMicrotasks(); + }); + + await expect(strongForm.submit()).rejects.toThrow(/already submitting/i); + assert.equal(onSubmitError.mock.calls.length, 1); + assert.match( + String(onSubmitError.mock.calls[0]?.[0]?.message), + /already submitting/i + ); + + submitorDeferred.resolve(undefined); + await firstSubmitPromise!; + + unmount(); + }); + + it("sets submitError + inputErrors on RequestBodyParseError and does not throw", async () => { + let strongForm: AnyStrongForm | undefined; + + const bodyParseError = Object.assign( + Object.create(RequestBodyParseError.prototype), + { + error: { formErrors: { name: "bad" } }, + } + ); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + { name?: string } + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => { + throw bodyParseError; + }, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await strongForm.submit(); + + await act(async () => { + await flushMicrotasks(); + }); + + assert.instanceOf(strongForm.submitError, Error); + assert.match( + String(strongForm.submitError?.message), + /field values are invalid/i + ); + assert.deepEqual(strongForm.inputErrors, { name: "bad" }); + + unmount(); + }); + + it("sets submitError + inputErrors on RequestQueryParamsParseError and does not throw", async () => { + let strongForm: AnyStrongForm | undefined; + + const queryParseError = Object.assign( + Object.create(RequestQueryParamsParseError.prototype), + { + error: { formErrors: { name: "bad" } }, + } + ); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + { name?: string } + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => { + throw queryParseError; + }, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await strongForm.submit(); + + await act(async () => { + await flushMicrotasks(); + }); + + assert.instanceOf(strongForm.submitError, Error); + assert.match( + String(strongForm.submitError?.message), + /query parameters are invalid/i + ); + assert.deepEqual(strongForm.inputErrors, { name: "bad" }); + + unmount(); + }); + + it("sets submitError + inputErrors and throws on ParseError", async () => { + let strongForm: AnyStrongForm | undefined; + + const parseError = Object.assign(Object.create(ParseError.prototype), { + error: { formErrors: { name: "bad" } }, + }); + + const onSubmitError = vi.fn(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + unknown, + { name?: string } + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => { + throw parseError; + }, + onSubmitError, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await expect(strongForm.submit()).rejects.toBe(parseError); + + await act(async () => { + await flushMicrotasks(); + }); + + assert.instanceOf(strongForm.submitError, Error); + assert.match( + String(strongForm.submitError?.message), + /response was invalid/i + ); + assert.deepEqual(strongForm.inputErrors, { name: "bad" }); + + assert.equal(onSubmitError.mock.calls.length, 1); + assert.equal(onSubmitError.mock.calls[0]?.[0], parseError); + + unmount(); + }); + + it("propagates unknown submit errors through onSubmitError", async () => { + let strongForm: AnyStrongForm | undefined; + + const failure = new Error("boom"); + const onSubmitError = vi.fn(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + unknown, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => { + throw failure; + }, + onSubmitError, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await expect(strongForm.submit()).rejects.toBe(failure); + assert.equal(onSubmitError.mock.calls.length, 1); + assert.equal(onSubmitError.mock.calls[0]?.[0], failure); + + unmount(); + }); + + it("throws unknown submit errors when no onSubmitError is provided", async () => { + let strongForm: AnyStrongForm | undefined; + + const failure = new Error("boom"); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + unknown, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => { + throw failure; + }, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + let caught: unknown; + await act(async () => { + await strongForm!.submit().catch((error) => { + caught = error; + }); + await flushMicrotasks(); + }); + + assert.equal(caught, failure); + + unmount(); + }); + + it("handleSubmit logs unexpected errors when no onSubmitError is provided", async () => { + let strongForm: AnyStrongForm | undefined; + + const consoleErrorSpy = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + unknown, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => { + throw new Error("boom"); + }, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + const preventDefault = vi.fn(); + await act(async () => { + strongForm!.handleSubmit({ preventDefault }); + await flushMicrotasks(); + }); + + assert.equal(preventDefault.mock.calls.length, 1); + assert.equal(consoleErrorSpy.mock.calls.length, 1); + + consoleErrorSpy.mockRestore(); + unmount(); + }); + + it("handleSubmit does not log when onSubmitError is provided", async () => { + let strongForm: AnyStrongForm | undefined; + + const consoleErrorSpy = vi + .spyOn(console, "error") + .mockImplementation(() => undefined); + + const onSubmitError = vi.fn(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + unknown, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => { + throw new Error("boom"); + }, + onSubmitError, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + const preventDefault = vi.fn(); + await act(async () => { + strongForm!.handleSubmit({ preventDefault }); + await flushMicrotasks(); + }); + + assert.equal(preventDefault.mock.calls.length, 1); + assert.equal(onSubmitError.mock.calls.length, 1); + assert.equal(consoleErrorSpy.mock.calls.length, 0); + + consoleErrorSpy.mockRestore(); + unmount(); + }); + + it("calls onSubmitSuccess on successful submit", async () => { + let strongForm: AnyStrongForm | undefined; + + const onSubmitSuccess = vi.fn(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + string, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => "done", + onSubmitSuccess, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await strongForm.submit(); + + await act(async () => { + await flushMicrotasks(); + }); + + assert.equal(onSubmitSuccess.mock.calls.length, 1); + + unmount(); + }); + + it("throws when onSubmitSuccess throws and no onSubmitError is provided", async () => { + let strongForm: AnyStrongForm | undefined; + + const failure = new Error("boom"); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + string, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => "done", + onSubmitSuccess: () => { + throw failure; + }, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + let caught: unknown; + await act(async () => { + await strongForm!.submit().catch((error) => { + caught = error; + }); + await flushMicrotasks(); + }); + + assert.equal(caught, failure); + + unmount(); + }); + + it("submits successfully when onSubmitSuccess is not provided", async () => { + let strongForm: AnyStrongForm | undefined; + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + string, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => "done", + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + + await strongForm.submit(); + + await act(async () => { + await flushMicrotasks(); + }); + + assert.equal(strongForm.submitOutput, "done"); + + unmount(); + }); + + it("isReady returns true when no validators are provided", async () => { + let strongForm: AnyStrongForm | undefined; + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + submitor: async () => undefined, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + assert.equal(strongForm.isReady, true); + + unmount(); + }); + + it("onRefineSuccess is called when refiner succeeds", async () => { + let strongForm: AnyStrongForm | undefined; + + const onRefineSuccess = vi.fn(); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + refiner: async (inputs) => inputs, + onRefineSuccess, + submitor: async () => undefined, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + assert.equal(onRefineSuccess.mock.calls.length, 1); + + unmount(); + }); + + it("onRefineError is called when refiner fails", async () => { + let strongForm: AnyStrongForm | undefined; + + const onRefineError = vi.fn(); + const error = new Error("nope"); + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "ok" })), + refiner: async () => { + throw error; + }, + onRefineError, + submitor: async () => undefined, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + assert.equal(onRefineError.mock.calls.length, 1); + assert.equal(onRefineError.mock.calls[0]?.[0], error); + + unmount(); + }); + + it("getFieldState reports required=false when validator has no required", async () => { + let strongForm: AnyStrongForm | undefined; + + const validators: Record = { + name: { url: true }, + }; + + const { unmount } = render( + React.createElement(function Harness() { + strongForm = useStrongForm< + Inputs, + Inputs, + Error, + unknown, + Error, + unknown + >({ + inputs: useConst(() => ({ name: "" })), + validators, + submitor: async () => undefined, + }); + return React.createElement("div"); + }) + ); + + await act(async () => { + await flushMicrotasks(); + }); + + assertAssigned(strongForm); + assert.equal(strongForm.getFieldState("name").isRequired, false); + + unmount(); + }); +}); diff --git a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/utils.test.ts b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/utils.test.ts new file mode 100644 index 000000000..d12ec03c5 --- /dev/null +++ b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/utils.test.ts @@ -0,0 +1,30 @@ +import { assert, describe, it } from "vitest"; + +import { isValueEmpty } from "../utils"; + +describe("StrongForm.utils.isValueEmpty", () => { + it("treats undefined/null as empty", () => { + assert.isTrue(isValueEmpty(undefined)); + assert.isTrue(isValueEmpty(null)); + }); + + it("treats empty/whitespace-only strings as empty", () => { + assert.isTrue(isValueEmpty("")); + assert.isTrue(isValueEmpty(" ")); + assert.isTrue(isValueEmpty("\n\t ")); + }); + + it("treats non-empty strings as not empty", () => { + assert.isFalse(isValueEmpty("a")); + assert.isFalse(isValueEmpty(" a ")); + assert.isFalse(isValueEmpty("0")); + }); + + it("treats non-string non-null values as not empty", () => { + assert.isFalse(isValueEmpty(0)); + assert.isFalse(isValueEmpty(false)); + assert.isFalse(isValueEmpty(true)); + assert.isFalse(isValueEmpty({})); + assert.isFalse(isValueEmpty([])); + }); +}); diff --git a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/validation.test.ts b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/validation.test.ts new file mode 100644 index 000000000..b92b6ab4b --- /dev/null +++ b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/__tests__/validation.test.ts @@ -0,0 +1,55 @@ +import { assert, describe, it } from "vitest"; + +import type { Validator } from "../validation"; +import { isRawInvalid } from "../validation"; + +describe("StrongForm.validation.isRawInvalid", () => { + it("returns false when no validator is provided", () => { + assert.isFalse(isRawInvalid(undefined, "anything")); + }); + + it("marks required empty values invalid", () => { + const v: Validator = { required: true }; + assert.isTrue(isRawInvalid(v, "")); + assert.isTrue(isRawInvalid(v, " ")); + assert.isTrue(isRawInvalid(v, null)); + assert.isTrue(isRawInvalid(v, undefined)); + }); + + it("does not mark optional URL empty values invalid", () => { + const v: Validator = { url: true }; + assert.isFalse(isRawInvalid(v, "")); + assert.isFalse(isRawInvalid(v, " ")); + assert.isFalse(isRawInvalid(v, null)); + assert.isFalse(isRawInvalid(v, undefined)); + }); + + it("marks non-string URL values invalid", () => { + const v: Validator = { url: true }; + assert.isTrue(isRawInvalid(v, 123)); + assert.isTrue(isRawInvalid(v, {})); + assert.isTrue(isRawInvalid(v, [])); + }); + + it("accepts http/https URLs", () => { + const v: Validator = { url: true }; + assert.isFalse(isRawInvalid(v, "https://example.com")); + assert.isFalse(isRawInvalid(v, "http://example.com/path?x=1")); + assert.isFalse(isRawInvalid(v, " https://example.com ")); + }); + + it("rejects non-http(s) or malformed URLs", () => { + const v: Validator = { url: true }; + assert.isTrue(isRawInvalid(v, "example.com")); + assert.isTrue(isRawInvalid(v, "ftp://example.com")); + assert.isTrue(isRawInvalid(v, "://broken")); + }); + + it("required + url requires non-empty and valid", () => { + const v: Validator = { required: true, url: true }; + assert.isTrue(isRawInvalid(v, "")); + assert.isTrue(isRawInvalid(v, " ")); + assert.isTrue(isRawInvalid(v, "ftp://example.com")); + assert.isFalse(isRawInvalid(v, "https://example.com")); + }); +}); diff --git a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/useStrongForm.ts b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/useStrongForm.ts index 39417d4a6..ca5c5672a 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/useStrongForm.ts +++ b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/useStrongForm.ts @@ -1,13 +1,11 @@ -import { useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { ParseError, RequestBodyParseError, RequestQueryParamsParseError, } from "../../../../../packages/thunderstore-api/src"; - -type Validator = { - required?: boolean; -}; +import type { Validator } from "./validation"; +import { isRawInvalid } from "./validation"; interface UseStrongFormProps< Inputs, @@ -21,7 +19,7 @@ interface UseStrongFormProps< * Validators for the form inputs. * * NOTE: If you add new validator types here, make sure to implement the - * validation logic in the `isReady` memo inside `useStrongForm`. + * validation logic in `isRawInvalid` in `validation.ts`. */ validators?: { [K in keyof Inputs]?: Validator; @@ -57,20 +55,99 @@ export function useStrongForm< const [submitOutput, setSubmitOutput] = useState(); const [submitError, setSubmitError] = useState(); const [inputErrors, setInputErrors] = useState(); + const [fieldInteractions, setFieldInteractions] = useState< + Partial< + Record< + keyof Inputs, + { + hasFocused: boolean; + hasBlurred: boolean; + } + > + > + >({}); + const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState(false); const isReady = useMemo(() => { if (!props.validators) return true; for (const key in props.validators) { const validator = props.validators[key]; - const value = props.inputs[key]; - // NOTE: Expand the checks as more validators are added - if (validator?.required) { - if (typeof value === "string" && value.trim() === "") return false; - if (value === undefined || value === null) return false; + const value = props.inputs[key as keyof Inputs]; + if (isRawInvalid(validator, value)) { + return false; } } return true; - }, [props.inputs]); + }, [props.inputs, props.validators]); + + const getFieldState = useCallback( + (field: K) => { + const validator = props.validators?.[field]; + const value = props.inputs[field]; + const isRequired = Boolean(validator?.required); + const rawInvalid = isRawInvalid(validator, value); + const interactions = fieldInteractions[field]; + const hasFinishedInteraction = + Boolean(interactions?.hasFocused && interactions?.hasBlurred) || + hasAttemptedSubmit; + const isInvalid = rawInvalid && hasFinishedInteraction; + return { + isRequired, + isInvalid, + }; + }, + [fieldInteractions, hasAttemptedSubmit, props.inputs, props.validators] + ); + + const markFieldInteraction = useCallback( + (field: keyof Inputs, type: "focus" | "blur") => { + setFieldInteractions((prev) => { + const current = prev[field] ?? { hasFocused: false, hasBlurred: false }; + const next = + type === "focus" + ? { ...current, hasFocused: true } + : { ...current, hasBlurred: true }; + if ( + current.hasFocused === next.hasFocused && + current.hasBlurred === next.hasBlurred + ) { + return prev; + } + return { ...prev, [field]: next }; + }); + }, + [] + ); + + const getFieldInteractionProps = useCallback( + (field: keyof Inputs) => ({ + onFocus: () => markFieldInteraction(field, "focus"), + onBlur: () => markFieldInteraction(field, "blur"), + }), + [markFieldInteraction] + ); + + const getFieldComponentProps = useCallback( + (field: keyof Inputs, options?: { disabled?: boolean }) => { + const fieldState = getFieldState(field); + const modifiers: ("invalid" | "disabled")[] = []; + if (fieldState.isInvalid) { + modifiers.push("invalid" as const); + } + if (options?.disabled) { + modifiers.push("disabled" as const); + } + const interactionProps = getFieldInteractionProps(field); + return { + ...interactionProps, + "aria-invalid": fieldState.isInvalid, + "aria-required": fieldState.isRequired, + csModifiers: modifiers, + required: fieldState.isRequired, + }; + }, + [getFieldInteractionProps, getFieldState] + ); useEffect(() => { if (refining || submitting) { @@ -106,6 +183,11 @@ export function useStrongForm< }, [props.inputs]); const submit = async () => { + setHasAttemptedSubmit(true); + + if (!isReady) { + return; + } if (submitting) { const error = new Error("Form is already submitting!"); if (props.onSubmitError) { @@ -153,38 +235,61 @@ export function useStrongForm< ) as SubmissionError ); setInputErrors(error.error.formErrors as InputErrors); - } else if (error instanceof RequestQueryParamsParseError) { + return; + } + if (error instanceof RequestQueryParamsParseError) { setSubmitError( new Error( "Some of the query parameters are invalid" ) as SubmissionError ); setInputErrors(error.error.formErrors as InputErrors); - } else if (error instanceof ParseError) { + return; + } + if (error instanceof ParseError) { setSubmitError( new Error( "Request succeeded, but the response was invalid" ) as SubmissionError ); setInputErrors(error.error.formErrors as InputErrors); - throw error; - } else { + if (props.onSubmitError) { + props.onSubmitError(error as SubmissionError); + } throw error; } + + if (props.onSubmitError) { + props.onSubmitError(error as SubmissionError); + } + throw error; }); return submitOutput; - } catch (error) { - if (props.onSubmitError) { - props.onSubmitError(error as SubmissionError); - } - throw error; } finally { setSubmitting(false); } }; + const handleSubmit = useCallback( + (e?: { preventDefault: () => void }) => { + e?.preventDefault(); + setHasAttemptedSubmit(true); + if (!isReady) { + return; + } + void submit().catch((error) => { + // Prevent unhandled rejections, but don't silently swallow unexpected errors. + if (!props.onSubmitError) { + console.error(error); + } + }); + }, + [isReady, submit] + ); + return { submit, + handleSubmit, submitting, submitOutput, submitError, @@ -193,5 +298,8 @@ export function useStrongForm< refineError, inputErrors, isReady, + getFieldState, + getFieldInteractionProps, + getFieldComponentProps, }; } diff --git a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/utils.ts b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/utils.ts new file mode 100644 index 000000000..d192c38f9 --- /dev/null +++ b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/utils.ts @@ -0,0 +1,6 @@ +export const isValueEmpty = (value: unknown) => { + if (typeof value === "string") { + return value.trim() === ""; + } + return value === undefined || value === null; +}; diff --git a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/validation.ts b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/validation.ts new file mode 100644 index 000000000..0b4446bab --- /dev/null +++ b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/validation.ts @@ -0,0 +1,47 @@ +import { z } from "zod"; + +import { isValueEmpty } from "./utils"; + +export type Validator = { + required?: boolean; + url?: boolean; +}; + +const httpUrlSchema = z + .string() + .trim() + .url() + .refine( + (value) => value.startsWith("http://") || value.startsWith("https://"), + { + message: "URL must start with http:// or https://", + } + ); + +function isValidHttpUrl(value: string): boolean { + return httpUrlSchema.safeParse(value).success; +} + +export function isRawInvalid( + validator: Validator | undefined, + value: unknown +): boolean { + if (!validator) return false; + + if (validator.required && isValueEmpty(value)) { + return true; + } + + if (validator.url) { + // URL is optional unless `required` is also set. + if (isValueEmpty(value)) { + return false; + } + if (typeof value !== "string") { + return true; + } + return !isValidHttpUrl(value); + } + + return false; +} diff --git a/apps/cyberstorm-remix/cyberstorm/utils/__tests__/dapperClientLoaders.test.ts b/apps/cyberstorm-remix/cyberstorm/utils/__tests__/dapperClientLoaders.test.ts new file mode 100644 index 000000000..410d5c13a --- /dev/null +++ b/apps/cyberstorm-remix/cyberstorm/utils/__tests__/dapperClientLoaders.test.ts @@ -0,0 +1,115 @@ +import { getSessionTools } from "cyberstorm/security/publicEnvVariables"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { DapperTs } from "@thunderstore/dapper-ts"; +import { ApiError } from "@thunderstore/thunderstore-api"; + +import { makeTeamSettingsTabLoader } from "../dapperClientLoaders"; + +vi.mock("cyberstorm/security/publicEnvVariables", () => ({ + getSessionTools: vi.fn().mockReturnValue({ + getConfig: vi.fn().mockReturnValue({ + apiHost: "http://api.example.invalid", + sessionId: "sid", + }), + clearInvalidSession: vi.fn(), + }), +})); + +vi.mock("@thunderstore/dapper-ts", () => ({ + DapperTs: vi.fn().mockImplementation((configFactory, removeSessionHook) => { + return { + __configFactory: configFactory, + __removeSessionHook: removeSessionHook, + getCurrentUser: vi.fn(), + }; + }), +})); + +describe("dapperClientLoaders", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("calls dataFetcher with dapper and teamName and merges return value", async () => { + const dataFetcher = vi.fn().mockResolvedValue({ foo: 123 }); + const loader = makeTeamSettingsTabLoader(dataFetcher); + + const result = await loader({ + params: { namespaceId: "MyTeam" }, + request: new Request("http://example.invalid"), + context: {}, + } as never); + + expect(result).toEqual({ teamName: "MyTeam", foo: 123 }); + expect(dataFetcher).toHaveBeenCalledTimes(1); + expect(dataFetcher.mock.calls[0][1]).toBe("MyTeam"); + expect(DapperTs).toHaveBeenCalledTimes(1); + + const dapperArg = dataFetcher.mock.calls[0][0] as unknown as { + __configFactory: () => unknown; + __removeSessionHook: () => void; + }; + expect(dapperArg.__configFactory()).toEqual({ + apiHost: "http://api.example.invalid", + sessionId: "sid", + }); + + const tools = (getSessionTools as unknown as ReturnType).mock + .results[0].value; + dapperArg.__removeSessionHook(); + expect(tools.clearInvalidSession).toHaveBeenCalledTimes(1); + }); + + it("translates ApiError with detail into a Response", async () => { + const dataFetcher = vi.fn().mockImplementation(() => { + throw new ApiError({ + message: "403: Forbidden", + response: new Response(null, { status: 403, statusText: "Forbidden" }), + responseJson: { detail: "Nope" }, + }); + }); + + const loader = makeTeamSettingsTabLoader(dataFetcher); + + let thrown: unknown; + try { + await loader({ + params: { namespaceId: "MyTeam" }, + request: new Request("http://example.invalid"), + context: {}, + } as never); + } catch (e) { + thrown = e; + } + + expect(thrown).toBeInstanceOf(Response); + const res = thrown as Response; + expect(res.status).toBe(403); + expect(await res.text()).toBe("Nope"); + }); + + it("uses response statusText when ApiError has no detail", async () => { + const dataFetcher = vi.fn().mockImplementation(() => { + throw new ApiError({ + message: "404: Not Found", + response: new Response(null, { status: 404, statusText: "Not Found" }), + }); + }); + + const loader = makeTeamSettingsTabLoader(dataFetcher); + + try { + await loader({ + params: { namespaceId: "MyTeam" }, + request: new Request("http://example.invalid"), + context: {}, + } as never); + } catch (e) { + const res = e as Response; + expect(res).toBeInstanceOf(Response); + expect(res.status).toBe(404); + expect(await res.text()).toBe("Not Found"); + } + }); +}); diff --git a/apps/cyberstorm-remix/cyberstorm/utils/__tests__/dapperSingleton.test.ts b/apps/cyberstorm-remix/cyberstorm/utils/__tests__/dapperSingleton.test.ts index 15b138c32..c6f6f3917 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/__tests__/dapperSingleton.test.ts +++ b/apps/cyberstorm-remix/cyberstorm/utils/__tests__/dapperSingleton.test.ts @@ -1,14 +1,16 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { DapperTs } from "@thunderstore/dapper-ts"; + +import type { Community } from "../../../../../packages/thunderstore-api/src"; +import * as publicEnvVariables from "../../security/publicEnvVariables"; import { - initializeClientDapper, getClientDapper, getDapperForRequest, + initializeClientDapper, resetDapperSingletonForTest, } from "../dapperSingleton"; import { deduplicatePromiseForRequest } from "../requestCache"; -import { DapperTs } from "@thunderstore/dapper-ts"; -import * as publicEnvVariables from "../../security/publicEnvVariables"; -import type { Community } from "../../../../../packages/thunderstore-api/src"; // Mock getSessionTools vi.mock("../../security/publicEnvVariables", () => ({ @@ -195,4 +197,25 @@ describe("dapperSingleton", () => { expect(mockGetCommunity).toHaveBeenCalledTimes(1); }); }); + + describe("resetDapperSingletonForTest", () => { + it("clears request-scoped proxy cache and config factory", () => { + initializeClientDapper(); + const request = new Request("http://localhost"); + + const proxy1 = getDapperForRequest(request); + // Ensure factory was resolved once + expect(publicEnvVariables.getSessionTools).toHaveBeenCalled(); + + resetDapperSingletonForTest(); + + // After reset, same request should produce a new proxy + const proxy2 = getDapperForRequest(request); + expect(proxy2).not.toBe(proxy1); + + // And config factory should be re-resolved + initializeClientDapper(); + expect(publicEnvVariables.getSessionTools).toHaveBeenCalled(); + }); + }); }); diff --git a/apps/cyberstorm-remix/cyberstorm/utils/__tests__/requestCache.test.ts b/apps/cyberstorm-remix/cyberstorm/utils/__tests__/requestCache.test.ts index 319925e4b..e55da2e00 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/__tests__/requestCache.test.ts +++ b/apps/cyberstorm-remix/cyberstorm/utils/__tests__/requestCache.test.ts @@ -1,4 +1,5 @@ -import { describe, it, expect, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; + import { deduplicatePromiseForRequest } from "../requestCache"; describe("requestCache", () => { diff --git a/apps/cyberstorm-remix/cyberstorm/utils/__tests__/sentry.test.ts b/apps/cyberstorm-remix/cyberstorm/utils/__tests__/sentry.test.ts index 573f052c8..1b9380820 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/__tests__/sentry.test.ts +++ b/apps/cyberstorm-remix/cyberstorm/utils/__tests__/sentry.test.ts @@ -1,4 +1,5 @@ import { assert, describe, it } from "vitest"; + import { denyUrls } from "../sentry"; // This attempts to match how Sentry does things. diff --git a/apps/cyberstorm-remix/cyberstorm/utils/dapperClientLoaders.ts b/apps/cyberstorm-remix/cyberstorm/utils/dapperClientLoaders.ts index 807439792..8ad43acf2 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/dapperClientLoaders.ts +++ b/apps/cyberstorm-remix/cyberstorm/utils/dapperClientLoaders.ts @@ -1,10 +1,9 @@ +import { getSessionTools } from "cyberstorm/security/publicEnvVariables"; import { type LoaderFunctionArgs } from "react-router"; import { DapperTs } from "@thunderstore/dapper-ts"; import { ApiError, type GenericApiError } from "@thunderstore/thunderstore-api"; -import { getSessionTools } from "cyberstorm/security/publicEnvVariables"; - /** * TODO * 1) This approach no longer handles different ApiErrors properly @@ -45,9 +44,12 @@ export function makeTeamSettingsTabLoader( const setupDapper = () => { const tools = getSessionTools(); - const config = tools?.getConfig(); - return new DapperTs(() => ({ - apiHost: config?.apiHost, - sessionId: config?.sessionId, - })); + const config = tools.getConfig(); + return new DapperTs( + () => ({ + apiHost: config.apiHost, + sessionId: config.sessionId, + }), + () => tools.clearInvalidSession() + ); }; diff --git a/apps/cyberstorm-remix/cyberstorm/utils/dapperSingleton.ts b/apps/cyberstorm-remix/cyberstorm/utils/dapperSingleton.ts index 0d1c0f735..2ac41c26a 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/dapperSingleton.ts +++ b/apps/cyberstorm-remix/cyberstorm/utils/dapperSingleton.ts @@ -1,5 +1,6 @@ import { DapperTs } from "@thunderstore/dapper-ts"; import { type RequestConfig } from "@thunderstore/thunderstore-api"; + import { getSessionTools } from "../security/publicEnvVariables"; import { deduplicatePromiseForRequest } from "./requestCache"; @@ -36,7 +37,10 @@ export function initializeClientDapper(factory?: ConfigFactory) { if (!window.Dapper) { const resolvedFactory = resolveConfigFactory(); - window.Dapper = new DapperTs(resolvedFactory); + const tools = getSessionTools(); + window.Dapper = new DapperTs(resolvedFactory, () => + tools.clearInvalidSession() + ); } } diff --git a/apps/cyberstorm-remix/cyberstorm/utils/semverCompare.ts b/apps/cyberstorm-remix/cyberstorm/utils/semverCompare.ts index 6b1139522..7fabf0724 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/semverCompare.ts +++ b/apps/cyberstorm-remix/cyberstorm/utils/semverCompare.ts @@ -1,7 +1,9 @@ +import semverCompare from "semver/functions/compare"; + import { type TableCompareColumnMeta } from "@thunderstore/cyberstorm"; -import { type TableRow } from "@thunderstore/cyberstorm/src/newComponents/Table/Table"; +import { type NewTableRow as TableRow } from "@thunderstore/cyberstorm"; + import { isSemver } from "./typeChecks"; -import semverCompare from "semver/functions/compare"; export function rowSemverCompare( a: TableRow, diff --git a/apps/cyberstorm-remix/entrypoint.dev.sh b/apps/cyberstorm-remix/entrypoint.dev.sh index 8238c382f..626dee574 100644 --- a/apps/cyberstorm-remix/entrypoint.dev.sh +++ b/apps/cyberstorm-remix/entrypoint.dev.sh @@ -33,21 +33,21 @@ if [ -z "$(ls -A node_modules)" ] || [ ! -f node_modules/.bin/react-router ]; th su node -c "yarn install --frozen-lockfile --production=false" fi -# Map localhost to the backend nginx container IP so SSR API calls hit the backend -# without changing client-side env values. +# Map thunderstore.localhost to the backend nginx container IP so SSR API calls hit +# the backend without changing client-side env values. backend_nginx_ip=$(getent hosts nginx | awk '{print $1}') if [ -n "$backend_nginx_ip" ]; then - # Prepend to /etc/hosts to take precedence over the default loopback entry. + # Prepend to /etc/hosts so it takes precedence. # Make idempotent to avoid duplicate lines on restarts. - if ! grep -q "^${backend_nginx_ip}[[:space:]][[:space:]]*localhost\([[:space:]]\|$\)" /etc/hosts 2>/dev/null; then + if ! grep -q "^${backend_nginx_ip}[[:space:]][[:space:]]*thunderstore\.localhost\([[:space:]]\|$\)" /etc/hosts 2>/dev/null; then tmp_hosts=$(mktemp) { - printf "%s localhost\n" "$backend_nginx_ip" - # Drop existing localhost mappings so the new mapping is unambiguous. - # This keeps other host entries intact. - awk '!($0 ~ /(^|[[:space:]])localhost([[:space:]]|$)/)' /etc/hosts 2>/dev/null || true + printf "%s thunderstore.localhost\n" "$backend_nginx_ip" + # Drop existing thunderstore.localhost mappings so the new mapping is unambiguous. + # This keeps other host entries (including localhost) intact. + awk '!($0 ~ /(^|[[:space:]])thunderstore\.localhost([[:space:]]|$)/)' /etc/hosts 2>/dev/null || true } > "$tmp_hosts" - cat "$tmp_hosts" > /etc/hosts || printf "%s localhost\n" "$backend_nginx_ip" >> /etc/hosts + cat "$tmp_hosts" > /etc/hosts || printf "%s thunderstore.localhost\n" "$backend_nginx_ip" >> /etc/hosts rm -f "$tmp_hosts" fi fi diff --git a/apps/cyberstorm-remix/package.json b/apps/cyberstorm-remix/package.json index 25b156ee4..720dffce7 100644 --- a/apps/cyberstorm-remix/package.json +++ b/apps/cyberstorm-remix/package.json @@ -2,7 +2,6 @@ "name": "@thunderstore/cyberstorm-remix", "version": "0.1.0", "private": true, - "sideEffects": false, "type": "module", "scripts": { "build": "react-router build", @@ -50,7 +49,7 @@ "playwright": "1.55.1", "typescript": "^5.6.2", "typescript-plugin-css-modules": "^5.1.0", - "vite": "7.1.7", + "vite": "^7.2.6", "vite-tsconfig-paths": "^5.0.1", "vitest": "3.2.4" }, diff --git a/apps/cyberstorm-remix/tsconfig.json b/apps/cyberstorm-remix/tsconfig.json index c3d4950a7..f72114524 100644 --- a/apps/cyberstorm-remix/tsconfig.json +++ b/apps/cyberstorm-remix/tsconfig.json @@ -33,6 +33,7 @@ "react": ["./node_modules/@types/react"] }, "verbatimModuleSyntax": true, + "disableSourceOfProjectReferenceRedirect": true, // Vite takes care of building everything, not tsc. "noEmit": true, diff --git a/apps/cyberstorm-remix/tsconfig.tsbuildinfo b/apps/cyberstorm-remix/tsconfig.tsbuildinfo new file mode 100644 index 000000000..e2e8c1d83 --- /dev/null +++ b/apps/cyberstorm-remix/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"root":["./react-router.config.ts","./vite.config.ts","./vitest.config.ts","./app/routes.ts","./app/commoncomponents/types.ts","./app/commoncomponents/errorboundary/index.ts","./app/commoncomponents/markdown/sanitize.ts","./cyberstorm/security/publicenvvariables.ts","./cyberstorm/session/__tests__/sessioncontext.test.ts","./cyberstorm/utils/htmlparsing.ts","./cyberstorm/utils/dapperclientloaders.ts","./cyberstorm/utils/dappersingleton.ts","./cyberstorm/utils/permissions.ts","./cyberstorm/utils/requestcache.ts","./cyberstorm/utils/searchparamsutils.ts","./cyberstorm/utils/semvercompare.ts","./cyberstorm/utils/sentry.ts","./cyberstorm/utils/typechecks.ts","./cyberstorm/utils/strongform/usestrongform.ts","./cyberstorm/utils/strongform/utils.ts","./cyberstorm/utils/strongform/validation.ts","./cyberstorm/utils/strongform/__tests__/usestrongform.test.ts","./cyberstorm/utils/strongform/__tests__/utils.test.ts","./cyberstorm/utils/strongform/__tests__/validation.test.ts","./cyberstorm/utils/__tests__/dapperclientloaders.test.ts","./cyberstorm/utils/__tests__/dappersingleton.test.ts","./cyberstorm/utils/__tests__/permissions.test.ts","./cyberstorm/utils/__tests__/requestcache.test.ts","./cyberstorm/utils/__tests__/sentry.test.ts","./app/entry.client.tsx","./app/entry.server.tsx","./app/healthz.tsx","./app/root.tsx","./app/c/community.tsx","./app/c/tabs/packagesearch/packagesearch.tsx","./app/commoncomponents/checkboxlist/checkboxlist.tsx","./app/commoncomponents/codeboxhtml/codeboxhtml.tsx","./app/commoncomponents/collapsible/collapsible.tsx","./app/commoncomponents/collapsibletext/collapsibletext.tsx","./app/commoncomponents/connection/connection.tsx","./app/commoncomponents/copybutton/copybutton.tsx","./app/commoncomponents/errorboundary/routeerrorboundary.tsx","./app/commoncomponents/footer/footer.tsx","./app/commoncomponents/listingdependency/listingdependency.tsx","./app/commoncomponents/loading/loading.tsx","./app/commoncomponents/markdown/markdown.tsx","./app/commoncomponents/navigation/navigation.tsx","./app/commoncomponents/navigation/navigationwrapper.tsx","./app/commoncomponents/notloggedin/notloggedin.tsx","./app/commoncomponents/packagesearch/packagesearch.tsx","./app/commoncomponents/packagesearch/components/packageorder.tsx","./app/commoncomponents/packagesearch/components/categorytagcloud/categorytagcloud.tsx","./app/commoncomponents/packagesearch/components/packagecount/packagecount.tsx","./app/commoncomponents/pageheader/pageheader.tsx","./app/commoncomponents/paginateddependencies/paginateddependencies.tsx","./app/commoncomponents/radiogroup/radiogroup.tsx","./app/commoncomponents/requiredindicator/requiredindicator.tsx","./app/commoncomponents/stalenessindicator/stalenessindicator.tsx","./app/communities/communities.tsx","./app/p/packageedit.tsx","./app/p/packagelisting.tsx","./app/p/packageversion.tsx","./app/p/packageversionwithoutcommunity.tsx","./app/p/components/reportpackage/reportpackagebutton.tsx","./app/p/components/reportpackage/reportpackageform.tsx","./app/p/components/reportpackage/reportpackagemodal.tsx","./app/p/components/reportpackage/reportpackagesubmitted.tsx","./app/p/components/reportpackage/usereportpackage.tsx","./app/p/components/teammembers/teammembers.tsx","./app/p/dependants/dependants.tsx","./app/p/tabs/changelog/changelog.tsx","./app/p/tabs/readme/packageversionreadme.tsx","./app/p/tabs/readme/packageversionwithoutcommunityreadme.tsx","./app/p/tabs/readme/readme.tsx","./app/p/tabs/required/packageversionrequired.tsx","./app/p/tabs/required/packageversionwithoutcommunityrequired.tsx","./app/p/tabs/required/required.tsx","./app/p/tabs/source/source.tsx","./app/p/tabs/versions/packageversionversions.tsx","./app/p/tabs/versions/packageversionwithoutcommunityversions.tsx","./app/p/tabs/versions/versions.tsx","./app/p/tabs/versions/common.tsx","./app/p/tabs/wiki/wiki.tsx","./app/p/tabs/wiki/wikicontent.tsx","./app/p/tabs/wiki/wikifirstpage.tsx","./app/p/tabs/wiki/wikinewpage.tsx","./app/p/tabs/wiki/wikipage.tsx","./app/p/tabs/wiki/wikipageedit.tsx","./app/p/team/team.tsx","./app/settings/teams/teams.tsx","./app/settings/teams/team/teamsettings.tsx","./app/settings/teams/team/tabs/members/memberaddform.tsx","./app/settings/teams/team/tabs/members/members.tsx","./app/settings/teams/team/tabs/members/memberstable.tsx","./app/settings/teams/team/tabs/profile/profile.tsx","./app/settings/teams/team/tabs/serviceaccounts/serviceaccountremovemodal.tsx","./app/settings/teams/team/tabs/serviceaccounts/serviceaccounts.tsx","./app/settings/teams/team/tabs/serviceaccounts/serviceaccountstable.tsx","./app/settings/teams/team/tabs/settings/settings.tsx","./app/settings/user/settings.tsx","./app/settings/user/account/account.tsx","./app/settings/user/connections/connections.tsx","./app/tools/manifest-validator/manifestvalidator.tsx","./app/tools/markdown-preview/markdownpreview.tsx","./app/tools/package-format-docs/packageformatdocs.tsx","./app/upload/upload.tsx","./cyberstorm/utils/linklibrary.tsx","./cyberstorm/utils/thunderstoreauth.tsx","./.react-router/types/+future.ts","./.react-router/types/+routes.ts","./.react-router/types/+server-build.d.ts","./.react-router/types/app/+types/healthz.ts","./.react-router/types/app/+types/root.ts","./.react-router/types/app/c/+types/community.ts","./.react-router/types/app/c/tabs/packagesearch/+types/packagesearch.ts","./.react-router/types/app/communities/+types/communities.ts","./.react-router/types/app/p/+types/packageedit.ts","./.react-router/types/app/p/+types/packagelisting.ts","./.react-router/types/app/p/+types/packageversion.ts","./.react-router/types/app/p/+types/packageversionwithoutcommunity.ts","./.react-router/types/app/p/dependants/+types/dependants.ts","./.react-router/types/app/p/tabs/changelog/+types/changelog.ts","./.react-router/types/app/p/tabs/readme/+types/packageversionreadme.ts","./.react-router/types/app/p/tabs/readme/+types/packageversionwithoutcommunityreadme.ts","./.react-router/types/app/p/tabs/readme/+types/readme.ts","./.react-router/types/app/p/tabs/required/+types/packageversionrequired.ts","./.react-router/types/app/p/tabs/required/+types/packageversionwithoutcommunityrequired.ts","./.react-router/types/app/p/tabs/required/+types/required.ts","./.react-router/types/app/p/tabs/source/+types/source.ts","./.react-router/types/app/p/tabs/versions/+types/packageversionversions.ts","./.react-router/types/app/p/tabs/versions/+types/packageversionwithoutcommunityversions.ts","./.react-router/types/app/p/tabs/versions/+types/versions.ts","./.react-router/types/app/p/tabs/wiki/+types/wiki.ts","./.react-router/types/app/p/tabs/wiki/+types/wikifirstpage.ts","./.react-router/types/app/p/tabs/wiki/+types/wikinewpage.ts","./.react-router/types/app/p/tabs/wiki/+types/wikipage.ts","./.react-router/types/app/p/tabs/wiki/+types/wikipageedit.ts","./.react-router/types/app/p/team/+types/team.ts","./.react-router/types/app/settings/teams/+types/teams.ts","./.react-router/types/app/settings/teams/team/+types/teamsettings.ts","./.react-router/types/app/settings/teams/team/tabs/members/+types/members.ts","./.react-router/types/app/settings/teams/team/tabs/profile/+types/profile.ts","./.react-router/types/app/settings/teams/team/tabs/serviceaccounts/+types/serviceaccounts.ts","./.react-router/types/app/settings/teams/team/tabs/settings/+types/settings.ts","./.react-router/types/app/settings/user/+types/settings.ts","./.react-router/types/app/settings/user/account/+types/account.ts","./.react-router/types/app/settings/user/connections/+types/connections.ts","./.react-router/types/app/tools/manifest-validator/+types/manifestvalidator.ts","./.react-router/types/app/tools/markdown-preview/+types/markdownpreview.ts","./.react-router/types/app/tools/package-format-docs/+types/packageformatdocs.ts","./.react-router/types/app/upload/+types/upload.ts"],"version":"5.8.3"} diff --git a/apps/cyberstorm-remix/vite.config.ts b/apps/cyberstorm-remix/vite.config.ts index b270e8a0d..23033dd85 100644 --- a/apps/cyberstorm-remix/vite.config.ts +++ b/apps/cyberstorm-remix/vite.config.ts @@ -1,7 +1,7 @@ import { reactRouter } from "@react-router/dev/vite"; +import { sentryVitePlugin } from "@sentry/vite-plugin"; import { defineConfig } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; -import { sentryVitePlugin } from "@sentry/vite-plugin"; export default defineConfig({ server: { diff --git a/apps/cyberstorm-remix/vitest.config.ts b/apps/cyberstorm-remix/vitest.config.ts index 46fdf7e85..19440af0a 100644 --- a/apps/cyberstorm-remix/vitest.config.ts +++ b/apps/cyberstorm-remix/vitest.config.ts @@ -1,6 +1,13 @@ import { defineProject } from "vitest/config"; +const cyberstormRoot = new URL("./cyberstorm", import.meta.url).pathname; + export default defineProject({ + resolve: { + alias: { + cyberstorm: cyberstormRoot, + }, + }, test: { include: ["**/__tests__/**/*.test.ts"], exclude: ["dist/**/*"], diff --git a/apps/storybook/.storybook/preview.tsx b/apps/storybook/.storybook/preview.tsx index f476b5de8..c2806622c 100644 --- a/apps/storybook/.storybook/preview.tsx +++ b/apps/storybook/.storybook/preview.tsx @@ -1,8 +1,10 @@ +import { Provider as RadixTooltip } from "@radix-ui/react-tooltip"; import type { Preview } from "@storybook/react-vite"; + import { LinkingProvider } from "@thunderstore/cyberstorm"; import "@thunderstore/cyberstorm-theme"; + import { LinkLibrary } from "../LinkLibrary"; -import { Provider as RadixTooltip } from "@radix-ui/react-tooltip"; import "./styles.css"; const preview: Preview = { diff --git a/apps/storybook/LinkLibrary.tsx b/apps/storybook/LinkLibrary.tsx index bd8bdcc11..07d5c6037 100644 --- a/apps/storybook/LinkLibrary.tsx +++ b/apps/storybook/LinkLibrary.tsx @@ -1,6 +1,7 @@ -import { LinkLibrary } from "@thunderstore/cyberstorm"; import React from "react"; +import { LinkLibrary } from "@thunderstore/cyberstorm"; + interface CyberstormLinkProps { children?: React.ReactNode; } diff --git a/apps/storybook/package.json b/apps/storybook/package.json index c90fa42cb..a20a2c326 100644 --- a/apps/storybook/package.json +++ b/apps/storybook/package.json @@ -31,7 +31,7 @@ "storybook": "9.1.8", "typescript": "^5.6.2", "typescript-eslint": "8.44.0", - "vite": "7.1.7" + "vite": "^7.2.6" }, "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/apps/storybook" } diff --git a/apps/storybook/src/stories/cyberstormComponents/AdContainer.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/AdContainer.stories.tsx index 2ca82ecab..d8473eb91 100644 --- a/apps/storybook/src/stories/cyberstormComponents/AdContainer.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/AdContainer.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { AdContainer } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/AdContainer", diff --git a/apps/storybook/src/stories/cyberstormComponents/Alert.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Alert.stories.tsx index 765d6dbb8..468648e62 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Alert.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Alert.stories.tsx @@ -1,10 +1,11 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewAlert } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { AlertSizesList, AlertVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Alert", diff --git a/apps/storybook/src/stories/cyberstormComponents/Avatar.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Avatar.stories.tsx index 0837930a4..dda374ee0 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Avatar.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Avatar.stories.tsx @@ -1,10 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewAvatar } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { AvatarSizesList, AvatarVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; + import catboy from "../assets/catboy.png"; const meta = { diff --git a/apps/storybook/src/stories/cyberstormComponents/BreadCrumbs.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/BreadCrumbs.stories.tsx index fe8c61ae8..cf1fe3331 100644 --- a/apps/storybook/src/stories/cyberstormComponents/BreadCrumbs.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/BreadCrumbs.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewBreadCrumbs, NewBreadCrumbsLink } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/BreadCrumbs", diff --git a/apps/storybook/src/stories/cyberstormComponents/Button.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Button.stories.tsx index 672a7f70d..0a4e8ea4f 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Button.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Button.stories.tsx @@ -1,13 +1,13 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; +import { NewButton } from "@thunderstore/cyberstorm"; // import { fn } from 'storybook/test'; import "@thunderstore/cyberstorm-theme"; -import { NewButton } from "@thunderstore/cyberstorm"; import { ButtonModifiersList, ButtonSizesList, ButtonVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export const meta = { diff --git a/apps/storybook/src/stories/cyberstormComponents/CardCommunity.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/CardCommunity.stories.tsx index 63de92cbf..821d42e69 100644 --- a/apps/storybook/src/stories/cyberstormComponents/CardCommunity.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/CardCommunity.stories.tsx @@ -1,6 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { CardCommunity } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; + import catHeim from "../assets/catheim.png"; const community = { diff --git a/apps/storybook/src/stories/cyberstormComponents/CardPackage.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/CardPackage.stories.tsx index d1c08a482..af015f723 100644 --- a/apps/storybook/src/stories/cyberstormComponents/CardPackage.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/CardPackage.stories.tsx @@ -1,8 +1,10 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { CardPackage } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; +import type { PackageListing } from "@thunderstore/dapper/types"; + import goblin from "../assets/goblin.png"; -import type { PackageListing } from "../../../../../packages/dapper/src/types"; const now = new Date("2023-01-01T00:00:00Z"); const modPackage = { diff --git a/apps/storybook/src/stories/cyberstormComponents/CodeBox.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/CodeBox.stories.tsx index 9c1e63704..668c44233 100644 --- a/apps/storybook/src/stories/cyberstormComponents/CodeBox.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/CodeBox.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; + import { CodeBox } from "@thunderstore/cyberstorm"; const meta = { diff --git a/apps/storybook/src/stories/cyberstormComponents/CodeInput.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/CodeInput.stories.tsx index d666c26b5..48db50016 100644 --- a/apps/storybook/src/stories/cyberstormComponents/CodeInput.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/CodeInput.stories.tsx @@ -1,11 +1,13 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { CodeInput } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { CodeInputModifiersList, CodeInputSizesList, CodeInputVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; + import "./CodeInput.css"; const meta = { diff --git a/apps/storybook/src/stories/cyberstormComponents/Drawer.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Drawer.stories.tsx index 9617af684..0f683e3c7 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Drawer.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Drawer.stories.tsx @@ -1,10 +1,11 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { Drawer, NewButton } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { DrawerSizesList, DrawerVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Drawer", diff --git a/apps/storybook/src/stories/cyberstormComponents/DropDown.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/DropDown.stories.tsx index 15c2c354c..0e06097eb 100644 --- a/apps/storybook/src/stories/cyberstormComponents/DropDown.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/DropDown.stories.tsx @@ -1,16 +1,17 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewButton, NewDropDown, - NewDropDownItem, NewDropDownDivider, + NewDropDownItem, } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { DropDownModifiersList, DropDownSizesList, DropDownVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/DropDown", diff --git a/apps/storybook/src/stories/cyberstormComponents/EmptyState.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/EmptyState.stories.tsx index 299ed18b2..9d7826f3e 100644 --- a/apps/storybook/src/stories/cyberstormComponents/EmptyState.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/EmptyState.stories.tsx @@ -1,8 +1,9 @@ +import { faSearch } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { EmptyState } from "@thunderstore/cyberstorm"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faSearch } from "@fortawesome/free-solid-svg-icons"; +import "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/EmptyState", diff --git a/apps/storybook/src/stories/cyberstormComponents/Heading.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Heading.stories.tsx index 7f3af77bf..71ed8b74c 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Heading.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Heading.stories.tsx @@ -1,11 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { Heading } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { - HeadingVariantsList, - HeadingSizesList, HeadingModifiersList, -} from "@thunderstore/cyberstorm-theme/src/components"; + HeadingSizesList, + HeadingVariantsList, +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Heading", diff --git a/apps/storybook/src/stories/cyberstormComponents/Icon.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Icon.stories.tsx index d189283df..9a0fca06e 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Icon.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Icon.stories.tsx @@ -1,10 +1,12 @@ +import { faStar } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { NewIcon } from "@thunderstore/cyberstorm"; import "@thunderstore/cyberstorm-theme"; +import { IconVariantsList } from "@thunderstore/cyberstorm-theme"; + import "./Icon.css"; -import { NewIcon } from "@thunderstore/cyberstorm"; -import { IconVariantsList } from "@thunderstore/cyberstorm-theme/src/components"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faStar } from "@fortawesome/free-solid-svg-icons"; const meta = { title: "Cyberstorm/Icon", diff --git a/apps/storybook/src/stories/cyberstormComponents/Image.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Image.stories.tsx index 7fc14627a..c4254f7c4 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Image.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Image.stories.tsx @@ -1,7 +1,9 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { Image } from "@thunderstore/cyberstorm"; -import { ImageVariantsList } from "@thunderstore/cyberstorm-theme/src/components"; +import "@thunderstore/cyberstorm-theme"; +import { ImageVariantsList } from "@thunderstore/cyberstorm-theme"; + import catHeim from "../assets/catheim.png"; const meta = { diff --git a/apps/storybook/src/stories/cyberstormComponents/Link.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Link.stories.tsx index 6479571ce..63fe96c39 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Link.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Link.stories.tsx @@ -1,7 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewLink } from "@thunderstore/cyberstorm"; -import { LinkVariantsList } from "@thunderstore/cyberstorm-theme/src/components"; +import "@thunderstore/cyberstorm-theme"; +import { LinkVariantsList } from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Link", diff --git a/apps/storybook/src/stories/cyberstormComponents/Menu.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Menu.stories.tsx index 28a18cc1c..1a5823fea 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Menu.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Menu.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { Menu, NewButton } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Menu", diff --git a/apps/storybook/src/stories/cyberstormComponents/MetaItem.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/MetaItem.stories.tsx index d6742e4eb..3f20dd7aa 100644 --- a/apps/storybook/src/stories/cyberstormComponents/MetaItem.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/MetaItem.stories.tsx @@ -1,10 +1,11 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewMetaItem } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { MetaItemSizesList, MetaItemVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/MetaItem", diff --git a/apps/storybook/src/stories/cyberstormComponents/Modal.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Modal.stories.tsx index c9e432376..4b31cdc48 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Modal.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Modal.stories.tsx @@ -1,10 +1,11 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { Modal, NewButton } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { ModalSizesList, ModalVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Modal", diff --git a/apps/storybook/src/stories/cyberstormComponents/Pagination.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Pagination.stories.tsx index bfcb7bf22..48dd1d2bf 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Pagination.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Pagination.stories.tsx @@ -1,9 +1,10 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; -import { NewButton } from "@thunderstore/cyberstorm"; import { useState } from "react"; + +import { NewButton } from "@thunderstore/cyberstorm"; import { NewPagination as Pagination } from "@thunderstore/cyberstorm"; -import type { PaginationProps } from "@thunderstore/cyberstorm/src/newComponents/Pagination/Pagination"; +import type { PaginationProps } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Pagination", diff --git a/apps/storybook/src/stories/cyberstormComponents/Select.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Select.stories.tsx index 42cef2234..aa4d075c8 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Select.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Select.stories.tsx @@ -1,11 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewSelect } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { SelectModifiersList, SelectSizesList, SelectVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Select", diff --git a/apps/storybook/src/stories/cyberstormComponents/SelectSearch.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/SelectSearch.stories.tsx index 2b67bcc5a..d1958be3c 100644 --- a/apps/storybook/src/stories/cyberstormComponents/SelectSearch.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/SelectSearch.stories.tsx @@ -1,16 +1,17 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; +import { useState } from "react"; + import { NewSelectSearch, type NewSelectSearchProps, type SelectOption, } from "@thunderstore/cyberstorm"; -import { useState } from "react"; +import "@thunderstore/cyberstorm-theme"; import { SelectSearchModifiersList, SelectSearchSizesList, SelectSearchVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/SelectSearch", diff --git a/apps/storybook/src/stories/cyberstormComponents/SkeletonBox.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/SkeletonBox.stories.tsx index e78b8a90b..b9b182174 100644 --- a/apps/storybook/src/stories/cyberstormComponents/SkeletonBox.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/SkeletonBox.stories.tsx @@ -1,6 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { SkeletonBox } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; + import "./SkeletonBox.css"; const meta = { diff --git a/apps/storybook/src/stories/cyberstormComponents/Switch.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Switch.stories.tsx index 39dbd0427..3c2bd9942 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Switch.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Switch.stories.tsx @@ -1,11 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewSwitch } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { SwitchModifiersList, SwitchSizesList, SwitchVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Switch", diff --git a/apps/storybook/src/stories/cyberstormComponents/Table.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Table.stories.tsx index c1481b7e6..3533200ba 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Table.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Table.stories.tsx @@ -1,11 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewTable } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { TableModifiersList, TableSizesList, TableVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Table", diff --git a/apps/storybook/src/stories/cyberstormComponents/Tabs.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Tabs.stories.tsx index 0ffdfe100..ac856abac 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Tabs.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Tabs.stories.tsx @@ -1,12 +1,13 @@ +import { faStar } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewIcon, Tabs } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { TabsSizesList, TabsVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faStar } from "@fortawesome/free-solid-svg-icons"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/Tabs", diff --git a/apps/storybook/src/stories/cyberstormComponents/Tag.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Tag.stories.tsx index 99bc2102f..d00c2c02e 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Tag.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Tag.stories.tsx @@ -1,11 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewTag } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { TagModifiersList, TagSizesList, TagVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const modes = ["tag", "button", "link"] as const; diff --git a/apps/storybook/src/stories/cyberstormComponents/TextAreaInput.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/TextAreaInput.stories.tsx index 560e9497d..ae3df34e1 100644 --- a/apps/storybook/src/stories/cyberstormComponents/TextAreaInput.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/TextAreaInput.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; + import { TextAreaInput } from "@thunderstore/cyberstorm"; const meta = { diff --git a/apps/storybook/src/stories/cyberstormComponents/TextInput.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/TextInput.stories.tsx index 39f89a23b..491d21249 100644 --- a/apps/storybook/src/stories/cyberstormComponents/TextInput.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/TextInput.stories.tsx @@ -1,11 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import "@thunderstore/cyberstorm-theme"; + import { NewTextInput } from "@thunderstore/cyberstorm"; +import "@thunderstore/cyberstorm-theme"; import { TextInputModifiersList, TextInputSizesList, TextInputVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; const meta = { title: "Cyberstorm/TextInput", diff --git a/apps/storybook/src/stories/cyberstormComponents/Toast.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Toast.stories.tsx index b96dfa06d..dbadd9df4 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Toast.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Toast.stories.tsx @@ -1,11 +1,11 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { Toast, ToastProvider } from "@thunderstore/cyberstorm"; import "@thunderstore/cyberstorm-theme"; -import * as ToastProvider from "@thunderstore/cyberstorm/src/newComponents/Toast"; import { ToastSizesList, ToastVariantsList, -} from "@thunderstore/cyberstorm-theme/src/components"; -import { Toast } from "@thunderstore/cyberstorm"; +} from "@thunderstore/cyberstorm-theme"; // Note: The default export from Toast has .Provider and .Viewport attached const meta = { @@ -19,9 +19,9 @@ const meta = { }, args: { children: "Hello toast!", id: "toast-1" }, render: (args) => ( - + - + ), } satisfies Meta; @@ -39,11 +39,11 @@ export const Variants: Story = { )); return ( - +
{toastVariants}
-
+ ); }, }; @@ -57,11 +57,11 @@ export const Sizes: Story = { )); return ( - +
{toastSizes}
-
+ ); }, }; diff --git a/apps/storybook/src/stories/cyberstormComponents/Tooltip.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/Tooltip.stories.tsx index 3a7b59440..a7c7508dd 100644 --- a/apps/storybook/src/stories/cyberstormComponents/Tooltip.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/Tooltip.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; + +import { NewButton, Tooltip } from "@thunderstore/cyberstorm"; import "@thunderstore/cyberstorm-theme"; -import { Tooltip, NewButton } from "@thunderstore/cyberstorm"; const meta = { title: "Cyberstorm/Tooltip", diff --git a/apps/storybook/src/stories/cyberstormComponents/ValidationBar.stories.tsx b/apps/storybook/src/stories/cyberstormComponents/ValidationBar.stories.tsx index f42547873..d5f35459b 100644 --- a/apps/storybook/src/stories/cyberstormComponents/ValidationBar.stories.tsx +++ b/apps/storybook/src/stories/cyberstormComponents/ValidationBar.stories.tsx @@ -1,5 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; + import { ValidationBar } from "@thunderstore/cyberstorm"; + import "./ValidationBar.css"; const meta = { diff --git a/apps/storybook/tsconfig.app.json b/apps/storybook/tsconfig.app.json index aac2b9acf..2ea2b7d9e 100644 --- a/apps/storybook/tsconfig.app.json +++ b/apps/storybook/tsconfig.app.json @@ -11,7 +11,7 @@ /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, + "verbatimModuleSyntax": false, "moduleDetection": "force", "noEmit": true, "jsx": "react-jsx", @@ -21,7 +21,17 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true + "noUncheckedSideEffectImports": true, + "paths": { + "@thunderstore/dapper/types": [ + "../../packages/dapper/src/types/index.ts" + ], + "@thunderstore/*": [ + "../../packages/*/src/index.ts", + "../../packages/*/src/index.tsx", + "../../packages/*" + ] + } }, "include": ["src"] } diff --git a/apps/storybook/tsconfig.node.json b/apps/storybook/tsconfig.node.json index 0c1e7b866..40a138756 100644 --- a/apps/storybook/tsconfig.node.json +++ b/apps/storybook/tsconfig.node.json @@ -10,7 +10,7 @@ /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, + "verbatimModuleSyntax": false, "moduleDetection": "force", "noEmit": true, diff --git a/apps/storybook/vite.config.ts b/apps/storybook/vite.config.ts index 0e43ae8de..f16e2be3f 100644 --- a/apps/storybook/vite.config.ts +++ b/apps/storybook/vite.config.ts @@ -1,5 +1,5 @@ -import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; // https://vite.dev/config/ export default defineConfig({ diff --git a/docker-compose.remix.development.yml b/docker-compose.remix.development.yml index 1c793c0e1..e604acb39 100644 --- a/docker-compose.remix.development.yml +++ b/docker-compose.remix.development.yml @@ -6,7 +6,7 @@ x-rsync-common: &rsync-common - workspace_src:/workspace - thunderstore_nginx_conf:/etc/nginx/user-conf environment: - RSYNC_ARGS: "-a --delete --info=progress2 --exclude=.git --exclude=build-secrets --exclude=.npmrc --exclude=node_modules --exclude=.turbo --exclude=.cache --exclude=apps/cyberstorm-remix/build --exclude=apps/cyberstorm-remix/.react-router" + RSYNC_ARGS: "-a --delete --info=progress2 --exclude=.git --exclude=build-secrets --exclude=.npmrc --exclude=node_modules --exclude=.turbo --exclude=.cache --exclude=packages/*/dist --exclude=packages/*/dist/** --exclude=packages/*/types/dist --exclude=packages/*/types/dist/** --exclude=apps/cyberstorm-remix/build --exclude=apps/cyberstorm-remix/.react-router" SYNC_INTERVAL: "5" services: @@ -15,7 +15,7 @@ services: container_name: cyberstorm-remix-sync restart: "no" command: >- - /bin/sh -c "set -e; apk add --no-cache rsync; rsync $$RSYNC_ARGS /src/ /workspace/; tmp=/etc/nginx/user-conf/.new-localhost.conf.tmp; cp -f /src/tools/nginx/new-localhost.conf \"$$tmp\"; mv -f \"$$tmp\" /etc/nginx/user-conf/new-localhost.conf; echo sync complete" + /bin/sh -c "set -e; apk add --no-cache rsync; rsync $$RSYNC_ARGS /src/ /workspace/; tmp=/etc/nginx/user-conf/.new-thunderstore-localhost.conf.tmp; cp -f /src/tools/nginx/new-thunderstore-localhost.conf \"$$tmp\"; mv -f \"$$tmp\" /etc/nginx/user-conf/new-thunderstore-localhost.conf; echo sync complete" cyberstorm-remix-watch: <<: *rsync-common @@ -25,7 +25,7 @@ services: cyberstorm-remix-sync: condition: service_completed_successfully command: >- - /bin/sh -c "set -e; apk add --no-cache rsync; while true; do rsync $$RSYNC_ARGS /src/ /workspace/; tmp=/etc/nginx/user-conf/.new-localhost.conf.tmp; cp -f /src/tools/nginx/new-localhost.conf \"$$tmp\"; mv -f \"$$tmp\" /etc/nginx/user-conf/new-localhost.conf; sleep $$SYNC_INTERVAL; done" + /bin/sh -c "set -e; apk add --no-cache rsync; while true; do rsync $$RSYNC_ARGS /src/ /workspace/; tmp=/etc/nginx/user-conf/.new-thunderstore-localhost.conf.tmp; cp -f /src/tools/nginx/new-thunderstore-localhost.conf \"$$tmp\"; mv -f \"$$tmp\" /etc/nginx/user-conf/new-thunderstore-localhost.conf; sleep $$SYNC_INTERVAL; done" cyberstorm-remix: container_name: cyberstorm-remix @@ -51,13 +51,13 @@ services: - NPM_CONFIG_USERCONFIG=/run/secrets/npmrc - ENABLE_BROKEN_PAGES=True - VITE_DEVELOPMENT=True - - VITE_SITE_URL=http://localhost - - VITE_BETA_SITE_URL=http://new.localhost - - VITE_API_URL=http://localhost - - VITE_COOKIE_DOMAIN=.localhost - - VITE_AUTH_BASE_URL=http://localhost - - VITE_AUTH_RETURN_URL=http://new.localhost - - __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=.localhost + - VITE_SITE_URL=http://thunderstore.localhost + - VITE_BETA_SITE_URL=http://new.thunderstore.localhost + - VITE_API_URL=http://thunderstore.localhost + - VITE_COOKIE_DOMAIN=.thunderstore.localhost + - VITE_AUTH_BASE_URL=http://thunderstore.localhost + - VITE_AUTH_RETURN_URL=http://new.thunderstore.localhost + - __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=.thunderstore.localhost secrets: - npmrc diff --git a/eslint.config.mjs b/eslint.config.mjs index 3dd6d7ecc..ea9cab7bc 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,12 +1,12 @@ +import { FlatCompat } from "@eslint/eslintrc"; +import js from "@eslint/js"; import typescriptEslint from "@typescript-eslint/eslint-plugin"; -import prettier from "eslint-plugin-prettier"; +import tsParser from "@typescript-eslint/parser"; import jsxA11Y from "eslint-plugin-jsx-a11y"; +import prettier from "eslint-plugin-prettier"; import globals from "globals"; -import tsParser from "@typescript-eslint/parser"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import js from "@eslint/js"; -import { FlatCompat } from "@eslint/eslintrc"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); diff --git a/package.json b/package.json index 2c0ddcce4..8561bc92c 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,14 @@ ], "scripts": { "postinstall": "preconstruct dev && manypkg check", - "build": "preconstruct build", + "build": "preconstruct build && yarn workspace @thunderstore/cyberstorm-theme build && yarn workspace @thunderstore/cyberstorm build && yarn workspace @thunderstore/ts-uploader build", "plop": "plop", "test": "vitest run", "test:watch": "vitest watch", - "coverage": "vitest run --coverage" + "coverage": "vitest run --coverage", + "tsc": "tsc -b", + "test:container": "node tools/scripts/run_test_container.mjs test", + "coverage:container": "node tools/scripts/run_test_container.mjs coverage" }, "engines": { "node": ">=20.17.0" @@ -27,6 +30,7 @@ "@manypkg/cli": "^0.21.4", "@microsoft/eslint-formatter-sarif": "^3.1.0", "@preconstruct/cli": "^2.8.7", + "@trivago/prettier-plugin-sort-imports": "6.0.0", "@types/node": "^20.16.0", "@typescript-eslint/eslint-plugin": "^8.7.0", "@typescript-eslint/parser": "^8.7.0", @@ -50,7 +54,7 @@ }, "preconstruct": { "packages": [ - "packages/*" + "packages/!(cyberstorm|ts-uploader|cyberstorm-theme)" ] } } diff --git a/packages/beta-switch/package.json b/packages/beta-switch/package.json index 651426d5a..1552d3d12 100644 --- a/packages/beta-switch/package.json +++ b/packages/beta-switch/package.json @@ -12,5 +12,8 @@ "scripts": { "build": "tsc", "dev": "tsc --watch" + }, + "dependencies": { + "@babel/runtime": "^7.25.6" } } diff --git a/packages/beta-switch/tsconfig.json b/packages/beta-switch/tsconfig.json index 51e494b37..71137caca 100644 --- a/packages/beta-switch/tsconfig.json +++ b/packages/beta-switch/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/cyberstorm-forms/package.json b/packages/cyberstorm-forms/package.json index ddf066908..78c7b5bb3 100644 --- a/packages/cyberstorm-forms/package.json +++ b/packages/cyberstorm-forms/package.json @@ -6,7 +6,7 @@ "type": "module", "main": "dist/thunderstore-cyberstorm-forms.cjs.js", "module": "dist/thunderstore-cyberstorm-forms.esm.js", - "types": "dist/thunderstore-cyberstorm-forms.cjs.d.ts", + "types": "dist/index.d.ts", "files": [ "dist" ], diff --git a/packages/cyberstorm-forms/src/actions/PackageLikeAction.tsx b/packages/cyberstorm-forms/src/actions/PackageLikeAction.tsx index 49463c221..8398992b7 100644 --- a/packages/cyberstorm-forms/src/actions/PackageLikeAction.tsx +++ b/packages/cyberstorm-forms/src/actions/PackageLikeAction.tsx @@ -1,10 +1,8 @@ -import { useFormToaster } from "@thunderstore/cyberstorm-forms"; +import { ApiError, packageRate } from "@thunderstore/thunderstore-api"; +import type { RequestConfig } from "@thunderstore/thunderstore-api"; import { ApiAction } from "@thunderstore/ts-api-react-actions"; -import { - ApiError, - RequestConfig, - packageRate, -} from "@thunderstore/thunderstore-api"; + +import { useFormToaster } from "../useFormToaster"; export function PackageLikeAction(props: { isLoggedIn: boolean; @@ -15,11 +13,14 @@ export function PackageLikeAction(props: { { state: "rated" | "unrated" }, { isLoggedIn: boolean; e: Error | ApiError | unknown } >({ - successMessage: (successProps) => + successMessage: (successProps: { state: "rated" | "unrated" }) => `${ successProps.state === "rated" ? "Liked" : "Removed like from" } package`, - errorMessage: (errorProps) => + errorMessage: (errorProps: { + isLoggedIn: boolean; + e: Error | ApiError | unknown; + }) => errorProps.isLoggedIn ? `Error: ${errorProps.e}` : "You must be logged in to like a package!", diff --git a/packages/cyberstorm-forms/tsconfig.json b/packages/cyberstorm-forms/tsconfig.json index afe00238d..095f39406 100644 --- a/packages/cyberstorm-forms/tsconfig.json +++ b/packages/cyberstorm-forms/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/cyberstorm-theme/package.json b/packages/cyberstorm-theme/package.json index 8fa9b4f4a..736b0e90c 100644 --- a/packages/cyberstorm-theme/package.json +++ b/packages/cyberstorm-theme/package.json @@ -4,10 +4,28 @@ "description": "Cyberstorm theme", "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/packages/cyberstorm-theme", "type": "module", - "main": "dist/thunderstore-cyberstorm-theme.cjs.js", - "module": "dist/thunderstore-cyberstorm-theme.esm.js", - "types": "dist/thunderstore-cyberstorm-theme.cjs.d.ts", + "main": "dist/thunderstore-cyberstorm-theme.umd.cjs", + "module": "dist/thunderstore-cyberstorm-theme.js", + "types": "dist/index.d.ts", + "style": "dist/cyberstorm-theme.css", + "scripts": { + "build": "vite build", + "dev": "vite build --watch" + }, "files": [ "dist" - ] + ], + "devDependencies": { + "vite": "^7.2.6", + "vite-plugin-dts": "^4.5.4" + }, + "exports": { + ".": { + "import": "./dist/thunderstore-cyberstorm-theme.js", + "require": "./dist/thunderstore-cyberstorm-theme.umd.cjs", + "types": "./dist/index.d.ts" + }, + "./css": "./dist/cyberstorm-theme.css", + "./package.json": "./package.json" + } } diff --git a/packages/cyberstorm-theme/src/components.tsx b/packages/cyberstorm-theme/src/components.tsx index 71ba4da8e..5fc39f190 100644 --- a/packages/cyberstorm-theme/src/components.tsx +++ b/packages/cyberstorm-theme/src/components.tsx @@ -132,6 +132,10 @@ export { export { type CardPackageVariants, CardPackageVariantsList, + type CardPackageSizes, + CardPackageSizesList, + type CardPackageModifiers, + CardPackageModifiersList, } from "./components/CardPackage/CardPackage"; export { type SelectSearchVariants, diff --git a/packages/cyberstorm-theme/src/components/Button/Button.css b/packages/cyberstorm-theme/src/components/Button/Button.css index 0564d25ad..6a4b0a50b 100644 --- a/packages/cyberstorm-theme/src/components/Button/Button.css +++ b/packages/cyberstorm-theme/src/components/Button/Button.css @@ -55,14 +55,14 @@ --button-text-color: var(--button-primary-text-color--default); --button-icon-color: var(--button-primary-icon-color--default); - &:where(:hover), + &:where(:hover:not(:disabled)), &[data-state="open"] { --button-background-color: var(--button-primary-bg-color--hover); --button-text-color: var(--button-primary-text-color--hover); --button-icon-color: var(--button-primary-icon-color--hover); } - &:where(:active) { + &:where(:active:not(:disabled)) { --button-background-color: var(--button-primary-bg-color--active); --button-text-color: var(--button-primary-text-color--active); --button-icon-color: var(--button-primary-icon-color--active); @@ -74,14 +74,14 @@ --button-text-color: var(--button-secondary-text-color--default); --button-icon-color: var(--button-secondary-icon-color--default); - &:where(:hover), + &:where(:hover:not(:disabled)), &[data-state="open"] { --button-background-color: var(--button-secondary-bg-color--hover); --button-text-color: var(--button-secondary-text-color--hover); --button-icon-color: var(--button-secondary-icon-color--hover); } - &:where(:active) { + &:where(:active:not(:disabled)) { --button-background-color: var(--button-secondary-bg-color--active); --button-text-color: var(--button-secondary-text-color--active); --button-icon-color: var(--button-secondary-icon-color--active); @@ -93,14 +93,14 @@ --button-text-color: var(--button-accent-text-color--default); --button-icon-color: var(--button-accent-icon-color--default); - &:where(:hover), + &:where(:hover:not(:disabled)), &[data-state="open"] { --button-background-color: var(--button-accent-bg-color--hover); --button-text-color: var(--button-accent-text-color--hover); --button-icon-color: var(--button-accent-icon-color--hover); } - &:where(:active) { + &:where(:active:not(:disabled)) { --button-background-color: var(--button-accent-bg-color--active); --button-text-color: var(--button-accent-text-color--active); --button-icon-color: var(--button-accent-icon-color--active); @@ -115,7 +115,7 @@ background: var(--button-special-background--default); - &:where(:hover), + &:where(:hover:not(:disabled)), &[data-state="open"] { --button-text-color: var(--button-special-text-color--hover); --button-icon-color: var(--button-special-icon-color--hover); @@ -125,7 +125,7 @@ background: var(--button-special-background--hover); } - &:where(:active) { + &:where(:active:not(:disabled)) { --button-text-color: var(--button-special-text-color--active); --button-icon-color: var(--button-special-icon-color--active); --button-border: var(--button-special-border--active); @@ -140,14 +140,14 @@ --button-text-color: var(--button-success-text-color--default); --button-icon-color: var(--button-success-icon-color--default); - &:where(:hover), + &:where(:hover:not(:disabled)), &[data-state="open"] { --button-background-color: var(--button-success-bg-color--hover); --button-text-color: var(--button-success-text-color--hover); --button-icon-color: var(--button-success-icon-color--hover); } - &:where(:active) { + &:where(:active:not(:disabled)) { --button-background-color: var(--button-success-bg-color--active); --button-text-color: var(--button-success-text-color--active); --button-icon-color: var(--button-success-icon-color--active); @@ -159,14 +159,14 @@ --button-text-color: var(--button-info-text-color--default); --button-icon-color: var(--button-info-icon-color--default); - &:where(:hover), + &:where(:hover:not(:disabled)), &[data-state="open"] { --button-background-color: var(--button-info-bg-color--hover); --button-text-color: var(--button-info-text-color--hover); --button-icon-color: var(--button-info-icon-color--hover); } - &:where(:active) { + &:where(:active:not(:disabled)) { --button-background-color: var(--button-info-bg-color--active); --button-text-color: var(--button-info-text-color--active); --button-icon-color: var(--button-info-icon-color--active); @@ -178,14 +178,14 @@ --button-text-color: var(--button-warning-text-color--default); --button-icon-color: var(--button-warning-icon-color--default); - &:where(:hover), + &:where(:hover:not(:disabled)), &[data-state="open"] { --button-background-color: var(--button-warning-bg-color--hover); --button-text-color: var(--button-warning-text-color--hover); --button-icon-color: var(--button-warning-icon-color--hover); } - &:where(:active) { + &:where(:active:not(:disabled)) { --button-background-color: var(--button-warning-bg-color--active); --button-text-color: var(--button-warning-text-color--active); --button-icon-color: var(--button-warning-icon-color--active); @@ -197,14 +197,14 @@ --button-text-color: var(--button-danger-text-color--default); --button-icon-color: var(--button-danger-icon-color--default); - &:where(:hover), + &:where(:hover:not(:disabled)), &[data-state="open"] { --button-background-color: var(--button-danger-bg-color--hover); --button-text-color: var(--button-danger-text-color--hover); --button-icon-color: var(--button-danger-icon-color--hover); } - &:where(:active) { + &:where(:active:not(:disabled)) { --button-background-color: var(--button-danger-bg-color--active); --button-text-color: var(--button-danger-text-color--active); --button-icon-color: var(--button-danger-icon-color--active); @@ -227,7 +227,6 @@ .button[disabled], .button[aria-disabled="true"] { opacity: 0.5; - pointer-events: none; } .button:where(.button--only-icon) { diff --git a/packages/cyberstorm-theme/src/components/TextInput/TextInput.css b/packages/cyberstorm-theme/src/components/TextInput/TextInput.css index 95cff966b..818b73f41 100644 --- a/packages/cyberstorm-theme/src/components/TextInput/TextInput.css +++ b/packages/cyberstorm-theme/src/components/TextInput/TextInput.css @@ -27,6 +27,10 @@ &:focus-within .text-input__left-icon { --text-input-left-icon-color: var(--input-icon-color--focus); } + + &.text-input__wrapper--invalid .text-input__left-icon { + --text-input-left-icon-color: var(--input-icon-color--invalid); + } } .text-input[value] { @@ -55,12 +59,24 @@ --right-padding-bonus: var(--space-16); } - .text-input:hover { + .text-input:where(.text-input--invalid), + .text-input:where(.text-input--invalid):hover, + .text-input:where(.text-input--invalid):focus-within { + --text-input-text-color: var(--input-text-color--invalid); + --text-input-background-color: var(--input-bg-color--invalid); + --text-input-border-color: var(--input-border-color--invalid); + } + + .text-input:disabled { + cursor: not-allowed; + } + + .text-input:hover:not(:disabled) { --text-input-background-color: var(--input-bg-color--hover); --text-input-border-color: var(--input-border-color--hover); } - .text-input:focus-within { + .text-input:focus-within:not(:disabled) { --text-input-text-color: var(--input-text-color--focus); --text-input-background-color: var(--input-bg-color--focus); --text-input-border-color: var(--input-border-color--focus); diff --git a/packages/cyberstorm-theme/src/components/componentsColors.css b/packages/cyberstorm-theme/src/components/componentsColors.css index 7f0604a23..7839e52ab 100644 --- a/packages/cyberstorm-theme/src/components/componentsColors.css +++ b/packages/cyberstorm-theme/src/components/componentsColors.css @@ -263,16 +263,20 @@ --input-bg-color--default: var(--color-nightsky-a4); --input-bg-color--focus: var(--color-nightsky-1); --input-bg-color--hover: var(--color-nightsky-a6); + --input-bg-color--invalid: var(--color-accent-red-2); --input-border-color--default: var(--color-nightsky-a10); --input-border-color--focus: var(--color-cyber-green-7); --input-border-color--hover: var(--color-nightsky-a10); + --input-border-color--invalid: var(--color-accent-red-7); --input-icon-color--default: var(--color-text-tertiary); --input-icon-color--focus: var(--color-text-secondary); --input-icon-color--hover: var(--color-text-tertiary); + --input-icon-color--invalid: var(--color-accent-red-8); --input-placeholder-color: var(--color-text-tertiary); --input-text-color--default: var(--color-text-secondary); --input-text-color--focus: var(--color-text-primary); --input-text-color--hover: var(--color-text-secondary); + --input-text-color--invalid: var(--color-text-primary); --kbd-bg-color--default: hsl(0deg 0 0% / 0); --kbd-border-color--default: var(--color-nightsky-a10); --kbd-text-color--default: var(--color-text-tertiary); diff --git a/packages/cyberstorm-theme/src/index.tsx b/packages/cyberstorm-theme/src/index.tsx index 362254fd1..25629e988 100644 --- a/packages/cyberstorm-theme/src/index.tsx +++ b/packages/cyberstorm-theme/src/index.tsx @@ -1,31 +1,33 @@ -import "./styles/colors.css"; -import "./styles/globals.css"; -import "./styles/typography.css"; -import "./styles/layout.css"; -import "./components/componentsColors.css"; -import "./components/componentsSizes.css"; -import "./components/componentsMiscs.css"; -import "./components/componentsLayouts.css"; +import "./components/AdContainer/AdContainer.css"; +import "./components/Alert/Alert.css"; +import "./components/Avatar/Avatar.css"; import "./components/Button/Button.css"; -import "./components/Heading/Heading.css"; -import "./components/DropDown/DropDown.css"; -import "./components/TextInput/TextInput.css"; +import "./components/CardPackage/CardPackage.css"; import "./components/CodeInput/CodeInput.css"; +import "./components/Drawer/Drawer.css"; +import "./components/DropDown/DropDown.css"; +import "./components/Heading/Heading.css"; import "./components/Icon/Icon.css"; +import "./components/Image/Image.css"; +import "./components/Link/Link.css"; +import "./components/MetaItem/MetaItem.css"; +import "./components/Modal/Modal.css"; +import "./components/Pagination/Pagination.css"; import "./components/Select/Select.css"; import "./components/SelectSearch/SelectSearch.css"; +import "./components/Switch/Switch.css"; import "./components/Table/Table.css"; +import "./components/Tabs/Tabs.css"; import "./components/Tag/Tag.css"; +import "./components/TextInput/TextInput.css"; import "./components/Toast/Toast.css"; -import "./components/Alert/Alert.css"; -import "./components/MetaItem/MetaItem.css"; -import "./components/Link/Link.css"; -import "./components/Image/Image.css"; -import "./components/AdContainer/AdContainer.css"; -import "./components/Pagination/Pagination.css"; -import "./components/Tabs/Tabs.css"; -import "./components/Modal/Modal.css"; -import "./components/CardPackage/CardPackage.css"; -import "./components/Drawer/Drawer.css"; -import "./components/Switch/Switch.css"; -import "./components/Avatar/Avatar.css"; +import "./components/componentsColors.css"; +import "./components/componentsLayouts.css"; +import "./components/componentsMiscs.css"; +import "./components/componentsSizes.css"; +import "./styles/colors.css"; +import "./styles/globals.css"; +import "./styles/layout.css"; +import "./styles/typography.css"; + +export * from "./components"; diff --git a/packages/cyberstorm-theme/tsconfig.json b/packages/cyberstorm-theme/tsconfig.json index 1043b8447..3df7a806f 100644 --- a/packages/cyberstorm-theme/tsconfig.json +++ b/packages/cyberstorm-theme/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, @@ -20,6 +20,9 @@ "resolveJsonModule": true, "forceConsistentCasingInFileNames": true, "composite": true, + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, "outDir": "./dist", "rootDir": "./src", "jsx": "react-jsx" diff --git a/packages/cyberstorm-theme/vite.config.ts b/packages/cyberstorm-theme/vite.config.ts new file mode 100644 index 000000000..41a972304 --- /dev/null +++ b/packages/cyberstorm-theme/vite.config.ts @@ -0,0 +1,21 @@ +import { resolve } from "path"; +import { defineConfig } from "vite"; +import dts from "vite-plugin-dts"; + +export default defineConfig({ + plugins: [ + dts({ + entryRoot: "src", + tsconfigPath: "./tsconfig.json", + }), + ], + build: { + lib: { + entry: resolve(__dirname, "src/index.tsx"), + name: "ThunderstoreCyberstormTheme", + fileName: (format) => + `thunderstore-cyberstorm-theme.${format === "es" ? "js" : "umd.cjs"}`, + formats: ["es", "umd"], + }, + }, +}); diff --git a/packages/cyberstorm/package.json b/packages/cyberstorm/package.json index 6fc5fda78..645e45d1d 100644 --- a/packages/cyberstorm/package.json +++ b/packages/cyberstorm/package.json @@ -1,18 +1,23 @@ { "name": "@thunderstore/cyberstorm", "version": "0.1.0", + "sideEffects": [ + "*.css", + "dist/*.css" + ], "description": "Shared components for Thunderstore", "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/packages/cyberstorm", "type": "module", - "main": "dist/thunderstore-cyberstorm.cjs.js", - "module": "dist/thunderstore-cyberstorm.esm.js", - "types": "dist/thunderstore-cyberstorm.cjs.d.ts", + "main": "dist/thunderstore-cyberstorm.umd.cjs", + "module": "dist/thunderstore-cyberstorm.js", + "types": "dist/index.d.ts", + "style": "dist/cyberstorm.css", "files": [ "dist" ], "scripts": { - "build": "tsc", - "dev": "tsc --watch" + "build": "vite build", + "dev": "vite build --watch" }, "dependencies": { "@babel/runtime": "^7.25.6", @@ -49,6 +54,21 @@ "devDependencies": { "@types/react-syntax-highlighter": "15.5.13", "typescript": "^5.6.2", - "typescript-plugin-css-modules": "^5.1.0" + "typescript-plugin-css-modules": "^5.1.0", + "vite": "^7.2.6", + "vite-plugin-dts": "^4.5.4" + }, + "exports": { + ".": { + "import": "./dist/thunderstore-cyberstorm.js", + "require": "./dist/thunderstore-cyberstorm.umd.cjs", + "types": "./dist/index.d.ts" + }, + "./css": { + "import": "./dist/cyberstorm.css", + "require": "./dist/cyberstorm.css", + "types": "./dist/index.d.ts" + }, + "./package.json": "./package.json" } } diff --git a/packages/cyberstorm/src/components/CodeInput/CodeInput.tsx b/packages/cyberstorm/src/components/CodeInput/CodeInput.tsx index e2c820ef6..fe5e89b5c 100644 --- a/packages/cyberstorm/src/components/CodeInput/CodeInput.tsx +++ b/packages/cyberstorm/src/components/CodeInput/CodeInput.tsx @@ -1,7 +1,7 @@ -import styles from "./CodeInput.module.css"; +import { classnames } from "../../utils/utils"; import { TextAreaInput } from "../TextAreaInput/TextAreaInput"; import { ValidationBar } from "../ValidationBar/ValidationBar"; -import { classnames } from "../../utils/utils"; +import styles from "./CodeInput.module.css"; interface CodeInputProps { value?: string; diff --git a/packages/cyberstorm/src/components/Links/Links.tsx b/packages/cyberstorm/src/components/Links/Links.tsx index df27c815d..88cdf6b09 100644 --- a/packages/cyberstorm/src/components/Links/Links.tsx +++ b/packages/cyberstorm/src/components/Links/Links.tsx @@ -6,6 +6,7 @@ * wishes. */ import React, { type PropsWithChildren } from "react"; + import { LinkingContext, type ThunderstoreLinkProps } from "./LinkingProvider"; interface typeWorkaroundProps extends PropsWithChildren { diff --git a/packages/cyberstorm/src/components/TextAreaInput/TextAreaInput.tsx b/packages/cyberstorm/src/components/TextAreaInput/TextAreaInput.tsx index c3150f6a7..280ec0bfe 100644 --- a/packages/cyberstorm/src/components/TextAreaInput/TextAreaInput.tsx +++ b/packages/cyberstorm/src/components/TextAreaInput/TextAreaInput.tsx @@ -1,4 +1,5 @@ import React from "react"; + import styles from "./TextAreaInput.module.css"; export interface TextAreaInputProps { diff --git a/packages/cyberstorm/src/components/ValidationBar/ValidationBar.tsx b/packages/cyberstorm/src/components/ValidationBar/ValidationBar.tsx index db078137e..89c3a9099 100644 --- a/packages/cyberstorm/src/components/ValidationBar/ValidationBar.tsx +++ b/packages/cyberstorm/src/components/ValidationBar/ValidationBar.tsx @@ -1,16 +1,16 @@ // TODO: Turn into non-module css -import styles from "./ValidationBar.module.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { + faArrowsRotate, faCircleCheck, - faTriangleExclamation, faPenToSquare, - faArrowsRotate, + faTriangleExclamation, } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { type ReactNode } from "react"; -import { classnames } from "../../utils/utils"; import { NewIcon } from "../.."; -import { type ReactNode } from "react"; +import { classnames } from "../../utils/utils"; +import styles from "./ValidationBar.module.css"; export function ValidationBar(props: { status: "waiting" | "processing" | "success" | "failure"; diff --git a/packages/cyberstorm/src/index.ts b/packages/cyberstorm/src/index.ts index d25c72c76..3cae7b6d4 100644 --- a/packages/cyberstorm/src/index.ts +++ b/packages/cyberstorm/src/index.ts @@ -1,4 +1,5 @@ import { config } from "@fortawesome/fontawesome-svg-core"; + config.autoAddCss = false; // primitiveComponents @@ -25,6 +26,7 @@ export { type InputTextAreaProps, } from "./primitiveComponents/Input/Input"; export { type PrimitiveComponentDefaultProps } from "./primitiveComponents/utils/utils"; +export { TooltipWrapper } from "./primitiveComponents/utils/utils"; // components export { CodeBox, type CodeBoxProps } from "./components/CodeBox/CodeBox"; @@ -81,6 +83,7 @@ export { Menu } from "./newComponents/Menu/Menu"; export { MetaItem as NewMetaItem } from "./newComponents/MetaItem/MetaItem"; export { Modal, type ModalProps } from "./newComponents/Modal/Modal"; export { Pagination as NewPagination } from "./newComponents/Pagination/Pagination"; +export type { PaginationProps } from "./newComponents/Pagination/Pagination"; export { Select as NewSelect, type SelectProps as NewSelectProps, @@ -97,7 +100,10 @@ export { type TextInputProps as NewTextInputProps, } from "./newComponents/TextInput/TextInput"; export { useToast } from "./newComponents/Toast/Provider"; +export { Provider as ToastProvider } from "./newComponents/Toast/Provider"; +export { Viewport as ToastViewport } from "./newComponents/Toast/Viewport"; export { Toast } from "./newComponents/Toast/Toast"; +export type { ToastProps } from "./newComponents/Toast/Toast"; export { Table as NewTable, TableSort as NewTableSort, @@ -114,6 +120,9 @@ export { isNode, isRecord, isStringArray } from "./utils/type_guards"; export type { SelectOption } from "./utils/types"; export { range, + classnames, + componentClasses, + numberWithSpaces, formatFileSize, formatInteger, formatToDisplayName, diff --git a/packages/cyberstorm/src/newComponents/AdContainer/AdContainer.tsx b/packages/cyberstorm/src/newComponents/AdContainer/AdContainer.tsx index 59421f4c2..228dd7cfb 100644 --- a/packages/cyberstorm/src/newComponents/AdContainer/AdContainer.tsx +++ b/packages/cyberstorm/src/newComponents/AdContainer/AdContainer.tsx @@ -1,7 +1,8 @@ -import "./AdContainer.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faHeart } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + import { NewIcon } from "../.."; +import "./AdContainer.css"; interface AdContainerProps { containerId: string; diff --git a/packages/cyberstorm/src/newComponents/Alert/Alert.tsx b/packages/cyberstorm/src/newComponents/Alert/Alert.tsx index 8bf83a464..c86df8d08 100644 --- a/packages/cyberstorm/src/newComponents/Alert/Alert.tsx +++ b/packages/cyberstorm/src/newComponents/Alert/Alert.tsx @@ -1,10 +1,3 @@ -import "./Alert.css"; -import React from "react"; -import { classnames, componentClasses } from "../../utils/utils"; -import { - type AlertSizes, - type AlertVariants, -} from "@thunderstore/cyberstorm-theme/src/components"; import { faCheckCircle, faExclamationCircle, @@ -12,7 +5,16 @@ import { } from "@fortawesome/free-solid-svg-icons"; import { faOctagonExclamation } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from "react"; + +import { + type AlertSizes, + type AlertVariants, +} from "@thunderstore/cyberstorm-theme"; + import { NewIcon, type PrimitiveComponentDefaultProps } from "../.."; +import { classnames, componentClasses } from "../../utils/utils"; +import "./Alert.css"; export interface AlertProps extends PrimitiveComponentDefaultProps { csVariant?: AlertVariants; diff --git a/packages/cyberstorm/src/newComponents/Avatar/Avatar.tsx b/packages/cyberstorm/src/newComponents/Avatar/Avatar.tsx index b39c32761..d9c0f85f5 100644 --- a/packages/cyberstorm/src/newComponents/Avatar/Avatar.tsx +++ b/packages/cyberstorm/src/newComponents/Avatar/Avatar.tsx @@ -1,12 +1,14 @@ -import "./Avatar.css"; +import { faUser } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + import { type AvatarSizes, type AvatarVariants, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; + import { NewIcon, type PrimitiveComponentDefaultProps } from "../.."; import { classnames, componentClasses } from "../../utils/utils"; -import { faUser } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import "./Avatar.css"; export interface AvatarProps extends PrimitiveComponentDefaultProps { csVariant?: AvatarVariants; diff --git a/packages/cyberstorm/src/newComponents/BreadCrumbs/BreadCrumbs.tsx b/packages/cyberstorm/src/newComponents/BreadCrumbs/BreadCrumbs.tsx index 39d7439c0..df04e2add 100644 --- a/packages/cyberstorm/src/newComponents/BreadCrumbs/BreadCrumbs.tsx +++ b/packages/cyberstorm/src/newComponents/BreadCrumbs/BreadCrumbs.tsx @@ -1,21 +1,22 @@ -import { memo, type PropsWithChildren } from "react"; - import { faHouse } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import "./BreadCrumbs.css"; -import { classnames, componentClasses } from "../../utils/utils"; +import { type PropsWithChildren, memo } from "react"; + +import { + type BreadCrumbsModifiers, + type BreadCrumbsSizes, + type BreadCrumbsVariants, +} from "@thunderstore/cyberstorm-theme"; + import { + type NewCyberstormLinkProps, NewIcon, NewLink, - type NewCyberstormLinkProps, type NewLinkProps, } from "../.."; import { Frame } from "../../primitiveComponents/Frame/Frame"; -import { - type BreadCrumbsVariants, - type BreadCrumbsSizes, - type BreadCrumbsModifiers, -} from "@thunderstore/cyberstorm-theme/src/components"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./BreadCrumbs.css"; type BreadCrumbsProps = PropsWithChildren<{ rootClasses?: string; diff --git a/packages/cyberstorm/src/newComponents/Button/Button.tsx b/packages/cyberstorm/src/newComponents/Button/Button.tsx index f5ea6bdd3..9d651e6ee 100644 --- a/packages/cyberstorm/src/newComponents/Button/Button.tsx +++ b/packages/cyberstorm/src/newComponents/Button/Button.tsx @@ -1,17 +1,19 @@ -import "./Button.css"; import { memo } from "react"; -import { classnames, componentClasses } from "../../utils/utils"; + +import { + type ButtonModifiers, + type ButtonSizes, + type ButtonVariants, +} from "@thunderstore/cyberstorm-theme"; + import { Actionable, type ActionableButtonProps, type ActionableCyberstormLinkProps, type ActionableLinkProps, } from "../../primitiveComponents/Actionable/Actionable"; -import { - type ButtonVariants, - type ButtonSizes, - type ButtonModifiers, -} from "@thunderstore/cyberstorm-theme/src/components"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./Button.css"; interface IButton { csVariant?: ButtonVariants; diff --git a/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.tsx b/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.tsx index d1c41ffaa..540b159c6 100644 --- a/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.tsx +++ b/packages/cyberstorm/src/newComponents/Card/CardCommunity/CardCommunity.tsx @@ -3,15 +3,16 @@ import { faDownload, faFire, } from "@fortawesome/free-solid-svg-icons"; +import { faSparkles } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { memo } from "react"; + import { type Community } from "@thunderstore/dapper/types"; -import { formatInteger } from "../../../utils/utils"; -import { NewLink, NewIcon, Image, NewTag, NewMetaItem } from "../../.."; +import { Image, NewIcon, NewLink, NewMetaItem, NewTag } from "../../.."; import { TooltipWrapper } from "../../../primitiveComponents/utils/utils"; +import { formatInteger } from "../../../utils/utils"; import "./CardCommunity.css"; -import { faSparkles } from "@fortawesome/pro-solid-svg-icons"; -import { memo } from "react"; interface Props { community: Community; diff --git a/packages/cyberstorm/src/newComponents/Card/CardPackage/CardPackage.tsx b/packages/cyberstorm/src/newComponents/Card/CardPackage/CardPackage.tsx index 3de06710b..fc5c19065 100644 --- a/packages/cyberstorm/src/newComponents/Card/CardPackage/CardPackage.tsx +++ b/packages/cyberstorm/src/newComponents/Card/CardPackage/CardPackage.tsx @@ -1,31 +1,32 @@ import { + faClockRotateLeft, + faCodeMerge, faDownload, faThumbTack, - faWarning, faThumbsUp, - faCodeMerge, - faClockRotateLeft, + faWarning, } from "@fortawesome/free-solid-svg-icons"; +import { faLips } from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import ago from "s-ago"; + +import { type CardPackageVariants } from "@thunderstore/cyberstorm-theme"; +import { + type CardPackageModifiers, + type CardPackageSizes, +} from "@thunderstore/cyberstorm-theme"; import { type PackageListing } from "@thunderstore/dapper/types"; +import { Image, NewIcon, NewLink, NewMetaItem, NewTag } from "../../.."; +import { RelativeTime } from "../../../components/RelativeTime/RelativeTime"; +import { TooltipWrapper } from "../../../primitiveComponents/utils/utils"; import { classnames, componentClasses, formatInteger, formatToDisplayName, } from "../../../utils/utils"; -import { NewLink, NewIcon, Image, NewTag, NewMetaItem } from "../../.."; -import { TooltipWrapper } from "../../../primitiveComponents/utils/utils"; import "./CardPackage.css"; -import { faLips } from "@fortawesome/pro-solid-svg-icons"; -import { RelativeTime } from "../../../components/RelativeTime/RelativeTime"; -import { type CardPackageVariants } from "@thunderstore/cyberstorm-theme/src/components"; -import ago from "s-ago"; -import { - type CardPackageModifiers, - type CardPackageSizes, -} from "@thunderstore/cyberstorm-theme/src/components/CardPackage/CardPackage"; interface Props { packageData: PackageListing; diff --git a/packages/cyberstorm/src/newComponents/CodeInput/CodeInput.tsx b/packages/cyberstorm/src/newComponents/CodeInput/CodeInput.tsx index 513833f41..0fce114d4 100644 --- a/packages/cyberstorm/src/newComponents/CodeInput/CodeInput.tsx +++ b/packages/cyberstorm/src/newComponents/CodeInput/CodeInput.tsx @@ -1,23 +1,25 @@ -import "./CodeInput.css"; -import React, { type ReactNode } from "react"; -import { - Input, - type InputTextAreaProps, -} from "../../primitiveComponents/Input/Input"; -import { classnames, componentClasses } from "../../utils/utils"; -import { NewIcon } from "../.."; import { - type CodeInputVariants, - type CodeInputSizes, - type CodeInputModifiers, -} from "@thunderstore/cyberstorm-theme/src/components"; -import { - faPenToSquare, faArrowsRotate, faCircleCheck, + faPenToSquare, faTriangleExclamation, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React, { type ReactNode } from "react"; + +import { + type CodeInputModifiers, + type CodeInputSizes, + type CodeInputVariants, +} from "@thunderstore/cyberstorm-theme"; + +import { NewIcon } from "../.."; +import { + Input, + type InputTextAreaProps, +} from "../../primitiveComponents/Input/Input"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./CodeInput.css"; export interface CodeInputProps extends Omit { diff --git a/packages/cyberstorm/src/newComponents/CycleButton/CycleButton.tsx b/packages/cyberstorm/src/newComponents/CycleButton/CycleButton.tsx index aa36cf856..88073e36f 100644 --- a/packages/cyberstorm/src/newComponents/CycleButton/CycleButton.tsx +++ b/packages/cyberstorm/src/newComponents/CycleButton/CycleButton.tsx @@ -1,10 +1,11 @@ -import "./CycleButton.css"; import React, { useState } from "react"; -import { classnames } from "../../utils/utils"; + import { Actionable, type ActionableButtonProps, } from "../../primitiveComponents/Actionable/Actionable"; +import { classnames } from "../../utils/utils"; +import "./CycleButton.css"; interface CycleButtonProps extends Omit { diff --git a/packages/cyberstorm/src/newComponents/Drawer/Drawer.tsx b/packages/cyberstorm/src/newComponents/Drawer/Drawer.tsx index 8b7c42b1b..4a545e0ce 100644 --- a/packages/cyberstorm/src/newComponents/Drawer/Drawer.tsx +++ b/packages/cyberstorm/src/newComponents/Drawer/Drawer.tsx @@ -1,18 +1,19 @@ +import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { type ReactNode } from "react"; -import "./Drawer.css"; +import { + type DrawerSizes, + type DrawerVariants, +} from "@thunderstore/cyberstorm-theme"; + import { NewButton, NewIcon } from "../.."; import { - type FramePopoverProps, Frame, + type FramePopoverProps, } from "../../primitiveComponents/Frame/Frame"; import { classnames, componentClasses } from "../../utils/utils"; -import { - type DrawerSizes, - type DrawerVariants, -} from "@thunderstore/cyberstorm-theme/src/components"; -import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import "./Drawer.css"; interface Props extends Omit { csVariant?: DrawerVariants; diff --git a/packages/cyberstorm/src/newComponents/DropDown/DropDown.tsx b/packages/cyberstorm/src/newComponents/DropDown/DropDown.tsx index d8a4eef08..e54cc242a 100644 --- a/packages/cyberstorm/src/newComponents/DropDown/DropDown.tsx +++ b/packages/cyberstorm/src/newComponents/DropDown/DropDown.tsx @@ -1,27 +1,28 @@ -import "./DropDown.css"; -import { type ReactNode, type ReactElement, memo } from "react"; - import { - Root, - Trigger, - Portal, Content, type DropdownMenuItemProps, Item, + Portal, + Root, + Trigger, } from "@radix-ui/react-dropdown-menu"; -import { classnames, componentClasses } from "../../utils/utils"; -import { type PrimitiveComponentDefaultProps } from "../../primitiveComponents/utils/utils"; +import { type ReactElement, type ReactNode, memo } from "react"; + import { - type DropDownVariants, - type DropDownSizes, - type DropDownModifiers, - type DropDownItemModifiers, - type DropDownItemSizes, - type DropDownItemVariants, type DropDownDividerModifiers, type DropDownDividerSizes, type DropDownDividerVariants, -} from "@thunderstore/cyberstorm-theme/src/components"; + type DropDownItemModifiers, + type DropDownItemSizes, + type DropDownItemVariants, + type DropDownModifiers, + type DropDownSizes, + type DropDownVariants, +} from "@thunderstore/cyberstorm-theme"; + +import { type PrimitiveComponentDefaultProps } from "../../primitiveComponents/utils/utils"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./DropDown.css"; interface DropDownProps extends PrimitiveComponentDefaultProps { defaultOpen?: boolean; diff --git a/packages/cyberstorm/src/newComponents/EmptyState/EmptyState.tsx b/packages/cyberstorm/src/newComponents/EmptyState/EmptyState.tsx index 0ae291219..e8e7e7d24 100644 --- a/packages/cyberstorm/src/newComponents/EmptyState/EmptyState.tsx +++ b/packages/cyberstorm/src/newComponents/EmptyState/EmptyState.tsx @@ -1,6 +1,7 @@ import { type PropsWithChildren } from "react"; -import "./EmptyState.css"; + import { classnames } from "../../utils/utils"; +import "./EmptyState.css"; interface Props extends PropsWithChildren { className?: string; diff --git a/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateIcon.tsx b/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateIcon.tsx index 271d9a468..be400e024 100644 --- a/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateIcon.tsx +++ b/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateIcon.tsx @@ -1,8 +1,9 @@ -import "./EmptyState.css"; -import { classnames } from "../../utils/utils"; -import { NewIcon } from "../.."; import { type ReactNode } from "react"; +import { NewIcon } from "../.."; +import { classnames } from "../../utils/utils"; +import "./EmptyState.css"; + interface Props { children: ReactNode | ReactNode[]; iconClasses?: string; diff --git a/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateMessage.tsx b/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateMessage.tsx index fcb8ea932..81701136e 100644 --- a/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateMessage.tsx +++ b/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateMessage.tsx @@ -1,6 +1,7 @@ import { type PropsWithChildren } from "react"; -import "./EmptyState.css"; + import { classnames } from "../../utils/utils"; +import "./EmptyState.css"; interface Props extends PropsWithChildren { className?: string; diff --git a/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateTitle.tsx b/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateTitle.tsx index 67ef9102e..005062404 100644 --- a/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateTitle.tsx +++ b/packages/cyberstorm/src/newComponents/EmptyState/EmptyStateTitle.tsx @@ -1,6 +1,7 @@ import { type PropsWithChildren } from "react"; -import "./EmptyState.css"; + import { classnames } from "../../utils/utils"; +import "./EmptyState.css"; interface Props extends PropsWithChildren { className?: string; diff --git a/packages/cyberstorm/src/newComponents/Heading/Heading.tsx b/packages/cyberstorm/src/newComponents/Heading/Heading.tsx index 0b5e35d99..07ca61b20 100644 --- a/packages/cyberstorm/src/newComponents/Heading/Heading.tsx +++ b/packages/cyberstorm/src/newComponents/Heading/Heading.tsx @@ -1,16 +1,18 @@ -import "./Heading.css"; import React from "react"; + +import { + type HeadingModifiers, + type HeadingSizes, + type HeadingVariants, +} from "@thunderstore/cyberstorm-theme"; + import { Frame, - type FrameHeadingProps, type FrameDisplayProps, + type FrameHeadingProps, } from "../../primitiveComponents/Frame/Frame"; import { classnames, componentClasses } from "../../utils/utils"; -import { - type HeadingVariants, - type HeadingSizes, - type HeadingModifiers, -} from "@thunderstore/cyberstorm-theme/src/components"; +import "./Heading.css"; interface DefaultProps extends React.HTMLAttributes, diff --git a/packages/cyberstorm/src/newComponents/Icon/Icon.tsx b/packages/cyberstorm/src/newComponents/Icon/Icon.tsx index a5c226cbb..3fea2f7d5 100644 --- a/packages/cyberstorm/src/newComponents/Icon/Icon.tsx +++ b/packages/cyberstorm/src/newComponents/Icon/Icon.tsx @@ -1,11 +1,13 @@ +import { memo } from "react"; + +import { type IconVariants } from "@thunderstore/cyberstorm-theme"; + import { Frame, type FrameIconProps, } from "../../primitiveComponents/Frame/Frame"; -import { memo } from "react"; -import "./Icon.css"; import { classnames, componentClasses } from "../../utils/utils"; -import { type IconVariants } from "@thunderstore/cyberstorm-theme/src/components"; +import "./Icon.css"; interface IconProps extends Omit { csVariant?: IconVariants; diff --git a/packages/cyberstorm/src/newComponents/Image/Image.tsx b/packages/cyberstorm/src/newComponents/Image/Image.tsx index 3c11bca1d..c9fe543f6 100644 --- a/packages/cyberstorm/src/newComponents/Image/Image.tsx +++ b/packages/cyberstorm/src/newComponents/Image/Image.tsx @@ -1,15 +1,16 @@ import { faBan, faGamepad } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { memo } from "react"; -import "./Image.css"; -import { classnames, componentClasses } from "../../utils/utils"; +import { type ImageVariants } from "@thunderstore/cyberstorm-theme"; + +import { NewIcon } from "../.."; import { Frame, type FrameWindowProps, } from "../../primitiveComponents/Frame/Frame"; -import { NewIcon } from "../.."; -import { memo } from "react"; -import { type ImageVariants } from "@thunderstore/cyberstorm-theme/src/components"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./Image.css"; interface ImageProps extends Omit { src: string | null | undefined; diff --git a/packages/cyberstorm/src/newComponents/Link/Link.tsx b/packages/cyberstorm/src/newComponents/Link/Link.tsx index 4b16e0a55..ef96159b8 100644 --- a/packages/cyberstorm/src/newComponents/Link/Link.tsx +++ b/packages/cyberstorm/src/newComponents/Link/Link.tsx @@ -1,12 +1,14 @@ -import "./Link.css"; -import { type LinkVariants } from "@thunderstore/cyberstorm-theme/src/components"; +import { memo } from "react"; + +import { type LinkVariants } from "@thunderstore/cyberstorm-theme"; + import { Actionable, type ActionableCyberstormLinkProps, type ActionableLinkProps, } from "../../primitiveComponents/Actionable/Actionable"; -import { memo } from "react"; import { classnames } from "../../utils/utils"; +import "./Link.css"; export interface LinkProps extends ActionableLinkProps { csVariant?: LinkVariants; diff --git a/packages/cyberstorm/src/newComponents/Menu/Menu.tsx b/packages/cyberstorm/src/newComponents/Menu/Menu.tsx index d1eb85d65..78987157f 100644 --- a/packages/cyberstorm/src/newComponents/Menu/Menu.tsx +++ b/packages/cyberstorm/src/newComponents/Menu/Menu.tsx @@ -1,15 +1,16 @@ +import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { type ReactNode } from "react"; -import "./Menu.css"; +import { type MenuVariants } from "@thunderstore/cyberstorm-theme"; + import { NewButton, NewIcon } from "../.."; import { - type FramePopoverProps, Frame, + type FramePopoverProps, } from "../../primitiveComponents/Frame/Frame"; import { classnames, componentClasses } from "../../utils/utils"; -import { type MenuVariants } from "@thunderstore/cyberstorm-theme/src/components"; -import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import "./Menu.css"; interface Props extends Omit { trigger?: ReactNode; diff --git a/packages/cyberstorm/src/newComponents/MetaItem/MetaItem.tsx b/packages/cyberstorm/src/newComponents/MetaItem/MetaItem.tsx index 2aab52f13..2c675fe04 100644 --- a/packages/cyberstorm/src/newComponents/MetaItem/MetaItem.tsx +++ b/packages/cyberstorm/src/newComponents/MetaItem/MetaItem.tsx @@ -1,18 +1,20 @@ -import "./MetaItem.css"; import React from "react"; -import { - Frame, - type FrameWindowProps, -} from "../../primitiveComponents/Frame/Frame"; -import { classnames, componentClasses } from "../../utils/utils"; + import { type MetaItemSizes, type MetaItemVariants, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; + import { Actionable, type ActionableButtonProps, } from "../../primitiveComponents/Actionable/Actionable"; +import { + Frame, + type FrameWindowProps, +} from "../../primitiveComponents/Frame/Frame"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./MetaItem.css"; interface MetaItemProps extends Omit { csVariant?: MetaItemVariants; diff --git a/packages/cyberstorm/src/newComponents/Modal/Modal.tsx b/packages/cyberstorm/src/newComponents/Modal/Modal.tsx index be89ca8b6..5767afc0a 100644 --- a/packages/cyberstorm/src/newComponents/Modal/Modal.tsx +++ b/packages/cyberstorm/src/newComponents/Modal/Modal.tsx @@ -1,22 +1,26 @@ +import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import * as Dialog from "@radix-ui/react-dialog"; import { - cloneElement, - isValidElement, type PropsWithChildren, type ReactNode, + cloneElement, + isValidElement, useCallback, useEffect, useRef, useState, } from "react"; -import "./Modal.css"; + +import { + type ModalSizes, + type ModalVariants, +} from "@thunderstore/cyberstorm-theme"; + import { NewButton, NewIcon } from "../.."; -import { type ModalVariants } from "@thunderstore/cyberstorm-theme/src/components"; import { classnames, componentClasses } from "../../utils/utils"; -import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; -import { type ModalSizes } from "@thunderstore/cyberstorm-theme/src/components/Modal/Modal"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import * as Dialog from "@radix-ui/react-dialog"; import { type ButtonComponentProps } from "../Button/Button"; +import "./Modal.css"; /** * Props for the `Modal` component. diff --git a/packages/cyberstorm/src/newComponents/Pagination/Pagination.tsx b/packages/cyberstorm/src/newComponents/Pagination/Pagination.tsx index b73d4f2e6..d64f189d3 100644 --- a/packages/cyberstorm/src/newComponents/Pagination/Pagination.tsx +++ b/packages/cyberstorm/src/newComponents/Pagination/Pagination.tsx @@ -1,10 +1,11 @@ -import "./Pagination.css"; import { faArrowLeft, faArrowRight } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { classnames, range } from "../../utils/utils"; import React from "react"; -import { Actionable } from "../../primitiveComponents/Actionable/Actionable"; + import { NewIcon } from "../.."; +import { Actionable } from "../../primitiveComponents/Actionable/Actionable"; +import { classnames, range } from "../../utils/utils"; +import "./Pagination.css"; export interface PaginationProps { currentPage: number; diff --git a/packages/cyberstorm/src/newComponents/Select/Select.tsx b/packages/cyberstorm/src/newComponents/Select/Select.tsx index 73609af06..054f98cc2 100644 --- a/packages/cyberstorm/src/newComponents/Select/Select.tsx +++ b/packages/cyberstorm/src/newComponents/Select/Select.tsx @@ -1,24 +1,25 @@ -import React, { memo, type ReactElement } from "react"; -import "./Select.css"; import { faCaretDown } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - import { - Root, Content, Item, Portal, + Root, Trigger, Viewport, } from "@radix-ui/react-select"; -import type { SelectOption } from "../../utils/types"; -import { classnames, componentClasses } from "../../utils/utils"; -import { NewButton, NewIcon } from "../.."; +import React, { type ReactElement, memo } from "react"; + import { type SelectModifiers, type SelectSizes, type SelectVariants, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; + +import { NewButton, NewIcon } from "../.."; +import type { SelectOption } from "../../utils/types"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./Select.css"; type _SelectProps = { variant?: "default" | "accent" | "wide"; diff --git a/packages/cyberstorm/src/newComponents/SelectSearch/SelectSearch.tsx b/packages/cyberstorm/src/newComponents/SelectSearch/SelectSearch.tsx index 2a251b6ca..768cc0482 100644 --- a/packages/cyberstorm/src/newComponents/SelectSearch/SelectSearch.tsx +++ b/packages/cyberstorm/src/newComponents/SelectSearch/SelectSearch.tsx @@ -1,19 +1,21 @@ -import React from "react"; -import "./SelectSearch.css"; -import type { SelectOption } from "../../utils/types"; -import { classnames, componentClasses } from "../../utils/utils"; -import { NewIcon, NewTag } from "../../index"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCaretDown, faCircleXmark, faXmark, } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from "react"; + import { - type SelectSearchVariants, - type SelectSearchSizes, type SelectSearchModifiers, -} from "@thunderstore/cyberstorm-theme/src/components"; + type SelectSearchSizes, + type SelectSearchVariants, +} from "@thunderstore/cyberstorm-theme"; + +import { NewIcon, NewTag } from "../../index"; +import type { SelectOption } from "../../utils/types"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./SelectSearch.css"; export type SelectSearchProps = | { diff --git a/packages/cyberstorm/src/newComponents/Switch/Switch.tsx b/packages/cyberstorm/src/newComponents/Switch/Switch.tsx index 802cb5bf5..b485e4a42 100644 --- a/packages/cyberstorm/src/newComponents/Switch/Switch.tsx +++ b/packages/cyberstorm/src/newComponents/Switch/Switch.tsx @@ -1,12 +1,14 @@ -import "./Switch.css"; -import React, { memo } from "react"; import * as RadixSwitch from "@radix-ui/react-switch"; -import { classnames, componentClasses } from "../../utils/utils"; +import React, { memo } from "react"; + import { - type SwitchVariants, - type SwitchSizes, type SwitchModifiers, -} from "@thunderstore/cyberstorm-theme/src/components"; + type SwitchSizes, + type SwitchVariants, +} from "@thunderstore/cyberstorm-theme"; + +import { classnames, componentClasses } from "../../utils/utils"; +import "./Switch.css"; export interface SwitchProps { value: boolean; diff --git a/packages/cyberstorm/src/newComponents/Table/Table.tsx b/packages/cyberstorm/src/newComponents/Table/Table.tsx index 6a6746025..3e235d2d3 100644 --- a/packages/cyberstorm/src/newComponents/Table/Table.tsx +++ b/packages/cyberstorm/src/newComponents/Table/Table.tsx @@ -1,19 +1,21 @@ -import { type CSSProperties, type ReactNode, useState } from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faSort, faSortDown, faSortUp, } from "@fortawesome/free-solid-svg-icons"; -import "./Table.css"; -import { NewIcon } from "../.."; -import { classnames, componentClasses } from "../../utils/utils"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { type CSSProperties, type ReactNode, useState } from "react"; +import React from "react"; + import { - type TableVariants, - type TableSizes, type TableModifiers, -} from "@thunderstore/cyberstorm-theme/src/components"; -import React from "react"; + type TableSizes, + type TableVariants, +} from "@thunderstore/cyberstorm-theme"; + +import { NewIcon } from "../.."; +import { classnames, componentClasses } from "../../utils/utils"; +import "./Table.css"; interface SortButtonProps { identifier: number; diff --git a/packages/cyberstorm/src/newComponents/Tabs/Tabs.tsx b/packages/cyberstorm/src/newComponents/Tabs/Tabs.tsx index a5b9c7fe5..673473832 100644 --- a/packages/cyberstorm/src/newComponents/Tabs/Tabs.tsx +++ b/packages/cyberstorm/src/newComponents/Tabs/Tabs.tsx @@ -1,14 +1,16 @@ -import "./Tabs.css"; import { memo } from "react"; + +import { + type TabsSizes, + type TabsVariants, +} from "@thunderstore/cyberstorm-theme"; + import { Frame, type FrameWindowProps, } from "../../primitiveComponents/Frame/Frame"; import { classnames, componentClasses } from "../../utils/utils"; -import { - type TabsSizes, - type TabsVariants, -} from "@thunderstore/cyberstorm-theme/src/components"; +import "./Tabs.css"; interface TabsProps extends Omit { csVariant?: TabsVariants; diff --git a/packages/cyberstorm/src/newComponents/Tag/Tag.tsx b/packages/cyberstorm/src/newComponents/Tag/Tag.tsx index a33f9b6e4..af7cf7f90 100644 --- a/packages/cyberstorm/src/newComponents/Tag/Tag.tsx +++ b/packages/cyberstorm/src/newComponents/Tag/Tag.tsx @@ -1,21 +1,23 @@ -import "./Tag.css"; import React from "react"; -import { - Frame, - type FrameWindowProps, -} from "../../primitiveComponents/Frame/Frame"; -import { classnames, componentClasses } from "../../utils/utils"; + import { type TagModifiers, type TagSizes, type TagVariants, -} from "@thunderstore/cyberstorm-theme/src/components"; +} from "@thunderstore/cyberstorm-theme"; + import { Actionable, type ActionableButtonProps, type ActionableCyberstormLinkProps, type ActionableLinkProps, } from "../../primitiveComponents/Actionable/Actionable"; +import { + Frame, + type FrameWindowProps, +} from "../../primitiveComponents/Frame/Frame"; +import { classnames, componentClasses } from "../../utils/utils"; +import "./Tag.css"; interface TagProps extends Omit { csMode?: "tag"; diff --git a/packages/cyberstorm/src/newComponents/TextInput/TextInput.tsx b/packages/cyberstorm/src/newComponents/TextInput/TextInput.tsx index 5316edf81..a4b05c28c 100644 --- a/packages/cyberstorm/src/newComponents/TextInput/TextInput.tsx +++ b/packages/cyberstorm/src/newComponents/TextInput/TextInput.tsx @@ -1,21 +1,23 @@ -import "./TextInput.css"; -import React, { memo, type ReactElement } from "react"; +import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React, { type ReactElement, memo } from "react"; + +import { + type TextInputModifiers, + type TextInputSizes, + type TextInputVariants, +} from "@thunderstore/cyberstorm-theme"; + +import { NewIcon } from "../.."; +import { Actionable } from "../../primitiveComponents/Actionable/Actionable"; +import { Frame } from "../../primitiveComponents/Frame/Frame"; import { Input, type InputTextAreaProps, type InputTextInputProps, } from "../../primitiveComponents/Input/Input"; import { classnames, componentClasses } from "../../utils/utils"; -import { Frame } from "../../primitiveComponents/Frame/Frame"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faXmarkLarge } from "@fortawesome/pro-solid-svg-icons"; -import { Actionable } from "../../primitiveComponents/Actionable/Actionable"; -import { NewIcon } from "../.."; -import { - type TextInputVariants, - type TextInputSizes, - type TextInputModifiers, -} from "@thunderstore/cyberstorm-theme/src/components"; +import "./TextInput.css"; export interface TextInputProps extends Omit { diff --git a/packages/cyberstorm/src/newComponents/Toast/Provider.tsx b/packages/cyberstorm/src/newComponents/Toast/Provider.tsx index 94bb39dfb..ab8242d6f 100644 --- a/packages/cyberstorm/src/newComponents/Toast/Provider.tsx +++ b/packages/cyberstorm/src/newComponents/Toast/Provider.tsx @@ -1,12 +1,13 @@ "use client"; import * as RadixToast from "@radix-ui/react-toast"; import { - createContext, type PropsWithChildren, + createContext, useContext, useReducer, } from "react"; import { v4 as uuid } from "uuid"; + import { type ToastProps } from "./Toast"; import { Viewport } from "./Viewport"; diff --git a/packages/cyberstorm/src/newComponents/Toast/Toast.tsx b/packages/cyberstorm/src/newComponents/Toast/Toast.tsx index 411a101c0..e56bb89ff 100644 --- a/packages/cyberstorm/src/newComponents/Toast/Toast.tsx +++ b/packages/cyberstorm/src/newComponents/Toast/Toast.tsx @@ -1,14 +1,3 @@ -import React from "react"; -import "./Toast.css"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import * as RadixToast from "@radix-ui/react-toast"; -import { NewIcon, type PrimitiveComponentDefaultProps } from "../.."; -import { - type ToastVariants, - type ToastSizes, -} from "@thunderstore/cyberstorm-theme/src/components"; -import { classnames, componentClasses } from "../../utils/utils"; - import { faCheckCircle, faExclamationCircle, @@ -18,6 +7,18 @@ import { faOctagonExclamation, faXmarkLarge, } from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import * as RadixToast from "@radix-ui/react-toast"; +import React from "react"; + +import { + type ToastSizes, + type ToastVariants, +} from "@thunderstore/cyberstorm-theme"; + +import { NewIcon, type PrimitiveComponentDefaultProps } from "../.."; +import { classnames, componentClasses } from "../../utils/utils"; +import "./Toast.css"; // export type ToastProps = { // variant?: "info" | "danger" | "warning" | "success"; diff --git a/packages/cyberstorm/src/newComponents/Toast/Viewport.tsx b/packages/cyberstorm/src/newComponents/Toast/Viewport.tsx index b48dfc09a..7f9f358e5 100644 --- a/packages/cyberstorm/src/newComponents/Toast/Viewport.tsx +++ b/packages/cyberstorm/src/newComponents/Toast/Viewport.tsx @@ -1,6 +1,7 @@ +import * as RadixToast from "@radix-ui/react-toast"; + import { Toast, type ToastProps } from "./Toast"; import "./Toast.css"; -import * as RadixToast from "@radix-ui/react-toast"; export function Viewport(props: { toasts: ToastProps[] }) { const { toasts } = props; diff --git a/packages/cyberstorm/src/newComponents/Toast/index.ts b/packages/cyberstorm/src/newComponents/Toast/index.ts index 61fc46657..a40691285 100644 --- a/packages/cyberstorm/src/newComponents/Toast/index.ts +++ b/packages/cyberstorm/src/newComponents/Toast/index.ts @@ -1,5 +1,5 @@ -import { Toast } from "./Toast"; import { Provider } from "./Provider"; +import { Toast } from "./Toast"; import { Viewport } from "./Viewport"; export type { ToastProps } from "./Toast"; diff --git a/packages/cyberstorm/src/newComponents/Tooltip/Tooltip.tsx b/packages/cyberstorm/src/newComponents/Tooltip/Tooltip.tsx index 6282b13ce..fb4c7dfd3 100644 --- a/packages/cyberstorm/src/newComponents/Tooltip/Tooltip.tsx +++ b/packages/cyberstorm/src/newComponents/Tooltip/Tooltip.tsx @@ -1,5 +1,5 @@ -import { memo, type ReactNode } from "react"; -import { Root, Trigger, Portal, Content, Arrow } from "@radix-ui/react-tooltip"; +import { Arrow, Content, Portal, Root, Trigger } from "@radix-ui/react-tooltip"; +import { type ReactNode, memo } from "react"; import "./Tooltip.css"; diff --git a/packages/cyberstorm/src/primitiveComponents/Actionable/Actionable.tsx b/packages/cyberstorm/src/primitiveComponents/Actionable/Actionable.tsx index 4102eb941..ab487a38c 100644 --- a/packages/cyberstorm/src/primitiveComponents/Actionable/Actionable.tsx +++ b/packages/cyberstorm/src/primitiveComponents/Actionable/Actionable.tsx @@ -1,9 +1,10 @@ +import React, { memo } from "react"; + +import { type ThunderstoreLinkProps } from "../../components/Links/LinkingProvider"; import { CyberstormLink, type CyberstormLinkIds, } from "../../components/Links/Links"; -import React, { memo } from "react"; -import { type ThunderstoreLinkProps } from "../../components/Links/LinkingProvider"; import { type PrimitiveComponentDefaultProps, TooltipWrapper, diff --git a/packages/cyberstorm/src/primitiveComponents/Frame/Frame.tsx b/packages/cyberstorm/src/primitiveComponents/Frame/Frame.tsx index 6b47ea812..2eb446b35 100644 --- a/packages/cyberstorm/src/primitiveComponents/Frame/Frame.tsx +++ b/packages/cyberstorm/src/primitiveComponents/Frame/Frame.tsx @@ -1,10 +1,11 @@ -import { classnames } from "./../../utils/utils"; -import React, { memo, type ReactNode } from "react"; +import React, { type ReactNode, memo } from "react"; +import { Children, cloneElement } from "react"; + import { type PrimitiveComponentDefaultProps, TooltipWrapper, } from "../utils/utils"; -import { Children, cloneElement } from "react"; +import { classnames } from "./../../utils/utils"; export interface FrameWindowProps extends React.HTMLAttributes, diff --git a/packages/cyberstorm/src/primitiveComponents/Input/Input.tsx b/packages/cyberstorm/src/primitiveComponents/Input/Input.tsx index 1ca39c80f..f0d5d575d 100644 --- a/packages/cyberstorm/src/primitiveComponents/Input/Input.tsx +++ b/packages/cyberstorm/src/primitiveComponents/Input/Input.tsx @@ -1,4 +1,5 @@ import React, { memo } from "react"; + import { type PrimitiveComponentDefaultProps, TooltipWrapper, diff --git a/packages/cyberstorm/src/primitiveComponents/utils/utils.tsx b/packages/cyberstorm/src/primitiveComponents/utils/utils.tsx index 11d0b6bae..a2957fc09 100644 --- a/packages/cyberstorm/src/primitiveComponents/utils/utils.tsx +++ b/packages/cyberstorm/src/primitiveComponents/utils/utils.tsx @@ -1,4 +1,5 @@ -import { memo, type PropsWithChildren } from "react"; +import { type PropsWithChildren, memo } from "react"; + import { Tooltip } from "../.."; interface TooltipWrapperProps extends PropsWithChildren { diff --git a/packages/cyberstorm/tsconfig.json b/packages/cyberstorm/tsconfig.json index afe00238d..bbb142e0b 100644 --- a/packages/cyberstorm/tsconfig.json +++ b/packages/cyberstorm/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, @@ -20,6 +20,9 @@ "resolveJsonModule": true, "forceConsistentCasingInFileNames": true, "composite": true, + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, "outDir": "./dist", "rootDir": "./src", "jsx": "react-jsx", @@ -27,7 +30,8 @@ { "name": "typescript-plugin-css-modules" } - ] + ], + "baseUrl": "." }, "include": ["./src/**/*.tsx", "./src/**/*.ts"], "exclude": ["node_modules"] diff --git a/packages/cyberstorm/vite.config.ts b/packages/cyberstorm/vite.config.ts new file mode 100644 index 000000000..4a7ebd4fa --- /dev/null +++ b/packages/cyberstorm/vite.config.ts @@ -0,0 +1,39 @@ +import { resolve } from "path"; +import { defineConfig } from "vite"; +import dts from "vite-plugin-dts"; + +import pkg from "./package.json"; + +const { dependencies, peerDependencies } = pkg as any; + +export default defineConfig({ + plugins: [ + dts({ + entryRoot: "src", + tsconfigPath: "./tsconfig.json", + }), + ], + build: { + lib: { + entry: resolve(__dirname, "src/index.ts"), + name: "ThunderstoreCyberstorm", + fileName: (format) => + `thunderstore-cyberstorm.${format === "es" ? "js" : "umd.cjs"}`, + formats: ["es", "umd"], + }, + rollupOptions: { + external: [ + ...Object.keys(peerDependencies || {}), + ...Object.keys(dependencies || {}), + "react/jsx-runtime", + ], + output: { + globals: { + react: "React", + "react-dom": "ReactDOM", + "styled-components": "styled", + }, + }, + }, + }, +}); diff --git a/packages/dapper-fake/src/fakers/community.ts b/packages/dapper-fake/src/fakers/community.ts index 89d36a806..6459add25 100644 --- a/packages/dapper-fake/src/fakers/community.ts +++ b/packages/dapper-fake/src/fakers/community.ts @@ -1,4 +1,5 @@ import { faker } from "@faker-js/faker"; + import { GetCommunities } from "@thunderstore/dapper/types"; import { diff --git a/packages/dapper-fake/src/fakers/package.ts b/packages/dapper-fake/src/fakers/package.ts index 9ed6585fb..8293e6b1a 100644 --- a/packages/dapper-fake/src/fakers/package.ts +++ b/packages/dapper-fake/src/fakers/package.ts @@ -1,8 +1,9 @@ import { faker } from "@faker-js/faker"; + import { PackageListingType } from "@thunderstore/dapper/types"; -import { getFakeImg, getFakePackageCategories, range, setSeed } from "./utils"; import { getFakeTeamMembers } from "./team"; +import { getFakeImg, getFakePackageCategories, range, setSeed } from "./utils"; // Content used to render one PackageCard in a list view. const getFakePackageListing = ( diff --git a/packages/dapper-fake/src/fakers/submission.ts b/packages/dapper-fake/src/fakers/submission.ts index 3135106b5..ea71ba4f7 100644 --- a/packages/dapper-fake/src/fakers/submission.ts +++ b/packages/dapper-fake/src/fakers/submission.ts @@ -1,6 +1,6 @@ import { - postPackageSubmission, fetchPackageSubmissionStatus, + postPackageSubmission, } from "@thunderstore/thunderstore-api"; const packageSubmissionStatus = { diff --git a/packages/dapper-fake/src/fakers/team.ts b/packages/dapper-fake/src/fakers/team.ts index ef658081a..6f5a0023c 100644 --- a/packages/dapper-fake/src/fakers/team.ts +++ b/packages/dapper-fake/src/fakers/team.ts @@ -1,8 +1,9 @@ import { faker } from "@faker-js/faker"; -import { getFakeImg, getIds, setSeed } from "./utils"; import { teamCreate } from "@thunderstore/thunderstore-api"; +import { getFakeImg, getIds, setSeed } from "./utils"; + export const getFakeTeamDetails = async (teamName: string) => { setSeed(teamName); diff --git a/packages/dapper-fake/src/index.ts b/packages/dapper-fake/src/index.ts index d90df5d92..75d70d8da 100644 --- a/packages/dapper-fake/src/index.ts +++ b/packages/dapper-fake/src/index.ts @@ -10,12 +10,14 @@ import { getFakePackageListingDetails, getFakePackageListings, getFakePackagePermissions, - getFakePackageVersionDependencies, - getFakePackageVersions, getFakePackageSource, + getFakePackageVersionDependencies, getFakePackageVersionDetails, + getFakePackageVersions, } from "./fakers/package"; import { getFakeServiceAccounts } from "./fakers/serviceAccount"; +import { postFakePackageSubmissionMetadata } from "./fakers/submission"; +import { getFakePackageSubmissionStatus } from "./fakers/submission"; import { getFakeTeamDetails, getFakeTeamMembers, @@ -25,8 +27,6 @@ import { getFakeCurrentUser, getFakeCurrentUserTeamPermissions, } from "./fakers/user"; -import { postFakePackageSubmissionMetadata } from "./fakers/submission"; -import { getFakePackageSubmissionStatus } from "./fakers/submission"; export class DapperFake implements DapperInterface { public getCommunities = getFakeCommunities; diff --git a/packages/dapper-fake/tsconfig.json b/packages/dapper-fake/tsconfig.json index 51e494b37..71137caca 100644 --- a/packages/dapper-fake/tsconfig.json +++ b/packages/dapper-fake/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/dapper-ts/package.json b/packages/dapper-ts/package.json index 2ae0ccbcb..9ede0bbea 100644 --- a/packages/dapper-ts/package.json +++ b/packages/dapper-ts/package.json @@ -6,7 +6,7 @@ "type": "module", "main": "dist/thunderstore-dapper-ts.cjs.js", "module": "dist/thunderstore-dapper-ts.esm.js", - "types": "dist/thunderstore-dapper-ts.cjs.d.ts", + "types": "dist/index.d.ts", "files": [ "dist" ], diff --git a/packages/dapper-ts/src/__tests__/index.test.ts b/packages/dapper-ts/src/__tests__/index.test.ts index 4378d3ad5..19b4f2a1a 100644 --- a/packages/dapper-ts/src/__tests__/index.test.ts +++ b/packages/dapper-ts/src/__tests__/index.test.ts @@ -1,4 +1,5 @@ -import { it, expect, beforeAll, test } from "vitest"; +import { beforeAll, expect, it, test } from "vitest"; + import { DapperTs } from "../index"; const communityId = "test-community-1"; @@ -9,7 +10,11 @@ let dapper: DapperTs; beforeAll(() => { dapper = new DapperTs(() => { - return { apiHost: "http://127.0.0.1:8000" }; + return { + apiHost: + import.meta.env.VITE_THUNDERSTORE_TEST_API_HOST ?? + "http://127.0.0.1:8000", + }; }); }); @@ -38,15 +43,15 @@ it("executes getPackageChangelog without errors", async () => { ).resolves.not.toThrowError(); }); -// TODO: Disabled temporarily until we decide on a testing strategy/policy regarding e2e tests -test.skip("executes getPackageVersionDependencies without errors", async () => { - await expect( - dapper.getPackageVersionDependencies( - namespaceId, - packageName, - packageVersion - ) - ).resolves.not.toThrowError(); +test("executes getPackageVersionDependencies without errors", async () => { + const response = await dapper.getPackageVersionDependencies( + namespaceId, + packageName, + packageVersion + ); + + expect(response.count).toBeTypeOf("number"); + expect(Array.isArray(response.results)).toBe(true); }); it("executes getPackageListingDetails without errors", async () => { @@ -77,11 +82,27 @@ it("executes getPackageReadme without errors", async () => { ).resolves.not.toThrowError(); }); -// TODO: Disabled temporarily until we decide on a testing strategy/policy regarding e2e tests -test.skip("executes getPackageSource without errors", async () => { - await expect( - dapper.getPackageSource(namespaceId, packageName) - ).resolves.not.toThrowError(); +test("executes getPackageSource when enabled (or 404 when disabled)", async () => { + try { + const response = await dapper.getPackageSource(namespaceId, packageName); + + // If the endpoint exists in the backend image, validate a minimal shape. + expect(response).toBeTruthy(); + expect(response).toHaveProperty("namespace"); + expect(response).toHaveProperty("package_name"); + expect(response).toHaveProperty("version_number"); + expect(response).toHaveProperty("is_visible"); + expect(response).toHaveProperty("decompilations"); + expect(Array.isArray(response.decompilations)).toBe(true); + } catch (err) { + // The test backend image used for containerized tests may not include the + // plugin that registers this endpoint. Treat a 404 as "endpoint disabled". + if (err instanceof Error && err.message.includes("404")) { + return; + } + + throw err; + } }); it("executes getPackageVersions without errors", async () => { diff --git a/packages/dapper-ts/src/index.ts b/packages/dapper-ts/src/index.ts index 7a7960eab..3f6fc613f 100644 --- a/packages/dapper-ts/src/index.ts +++ b/packages/dapper-ts/src/index.ts @@ -1,37 +1,37 @@ -import { DapperInterface } from "@thunderstore/dapper"; -import { RequestConfig } from "@thunderstore/thunderstore-api"; +import type { DapperInterface } from "@thunderstore/dapper"; +import type { RequestConfig } from "@thunderstore/thunderstore-api"; -import { getDynamicHTML } from "./methods/dynamicHTML"; import { getCommunities, getCommunity } from "./methods/communities"; import { getCommunityFilters } from "./methods/communityFilters"; -import { getRatedPackages } from "./methods/ratedPackages"; import { getCurrentUser, getCurrentUserTeamPermissions, } from "./methods/currentUser"; +import { getDynamicHTML } from "./methods/dynamicHTML"; import { getPackageChangelog, + getPackagePermissions, getPackageReadme, - getPackageVersions, - postPackageSubmissionMetadata, + getPackageSource, getPackageSubmissionStatus, + getPackageVersionDependencies, + getPackageVersions, getPackageWiki, getPackageWikiPage, - getPackagePermissions, - getPackageSource, - getPackageVersionDependencies, + postPackageSubmissionMetadata, } from "./methods/package"; import { getPackageListingDetails, getPackageListings, } from "./methods/packageListings"; +import { getPackageVersionDetails } from "./methods/packageVersion"; +import { getRatedPackages } from "./methods/ratedPackages"; import { getTeamDetails, getTeamMembers, getTeamServiceAccounts, postTeamCreate, } from "./methods/team"; -import { getPackageVersionDetails } from "./methods/packageVersion"; export interface DapperTsInterface extends DapperInterface { config: () => RequestConfig; @@ -68,7 +68,7 @@ export class DapperTs implements DapperTsInterface { this.getTeamDetails = this.getTeamDetails.bind(this); this.getTeamMembers = this.getTeamMembers.bind(this); this.getTeamServiceAccounts = this.getTeamServiceAccounts.bind(this); - this.postTeamCreate = () => this.postTeamCreate.bind(this); + this.postTeamCreate = this.postTeamCreate.bind(this); this.postPackageSubmissionMetadata = this.postPackageSubmissionMetadata.bind(this); this.getPackageSubmissionStatus = @@ -100,3 +100,30 @@ export class DapperTs implements DapperTsInterface { public postPackageSubmissionMetadata = postPackageSubmissionMetadata; public getPackageSubmissionStatus = getPackageSubmissionStatus; } + +export { + getCommunities, + getCommunity, + getCommunityFilters, + getCurrentUser, + getCurrentUserTeamPermissions, + getDynamicHTML, + getPackageChangelog, + getPackageListingDetails, + getPackageListings, + getPackagePermissions, + getPackageReadme, + getPackageSource, + getPackageSubmissionStatus, + getPackageVersionDependencies, + getPackageVersionDetails, + getPackageVersions, + getPackageWiki, + getPackageWikiPage, + getRatedPackages, + getTeamDetails, + getTeamMembers, + getTeamServiceAccounts, + postPackageSubmissionMetadata, + postTeamCreate, +}; diff --git a/packages/dapper-ts/src/methods/__tests__/currentUser.test.ts b/packages/dapper-ts/src/methods/__tests__/currentUser.test.ts new file mode 100644 index 000000000..9e188bc8b --- /dev/null +++ b/packages/dapper-ts/src/methods/__tests__/currentUser.test.ts @@ -0,0 +1,136 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import type { DapperTsInterface } from "../../index"; + +vi.mock("@thunderstore/thunderstore-api", () => { + class ApiError extends Error { + response: Response; + responseJson?: unknown; + + constructor(args: { + message: string; + response: Response; + responseJson?: unknown; + }) { + super(args.message); + this.name = "ApiError"; + this.response = args.response; + this.responseJson = args.responseJson; + } + } + + return { + ApiError, + fetchCurrentUser: vi.fn(), + fetchCurrentUserTeamPermissions: vi.fn(), + }; +}); + +import type { Mock } from "vitest"; +import { + ApiError, + fetchCurrentUser, + fetchCurrentUserTeamPermissions, +} from "@thunderstore/thunderstore-api"; + +import { getCurrentUser, getCurrentUserTeamPermissions } from "../currentUser"; + +describe("dapper-ts currentUser methods", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("getCurrentUser returns fetched data", async () => { + const ctx = { + config: () => ({ apiHost: "http://api", sessionId: "sid" }), + removeSessionHook: vi.fn(), + } satisfies Pick; + + const fetchCurrentUserMock = fetchCurrentUser as unknown as Mock; + + fetchCurrentUserMock.mockResolvedValue({ username: "abc" }); + + await expect( + getCurrentUser.call(ctx as unknown as DapperTsInterface) + ).resolves.toEqual({ + username: "abc", + }); + + expect(fetchCurrentUser).toHaveBeenCalledWith({ + config: ctx.config, + params: {}, + data: {}, + queryParams: {}, + }); + }); + + it("getCurrentUser clears session hook and returns null on 401 ApiError", async () => { + const removeSessionHook = vi.fn(); + const ctx = { + config: () => ({ apiHost: "http://api", sessionId: "sid" }), + removeSessionHook, + } satisfies Pick; + + const fetchCurrentUserMock = fetchCurrentUser as unknown as Mock; + + const response = new Response(null, { + status: 401, + statusText: "Unauthorized", + }); + const err = new ApiError({ message: "401: Unauthorized", response }); + + fetchCurrentUserMock.mockRejectedValue(err); + + await expect( + getCurrentUser.call(ctx as unknown as DapperTsInterface) + ).resolves.toBeNull(); + expect(removeSessionHook).toHaveBeenCalledTimes(1); + }); + + it("getCurrentUser rethrows non-401 ApiError", async () => { + const ctx = { + config: () => ({ apiHost: "http://api", sessionId: "sid" }), + removeSessionHook: vi.fn(), + } satisfies Pick; + + const fetchCurrentUserMock = fetchCurrentUser as unknown as Mock; + + const response = new Response(null, { + status: 500, + statusText: "Server Error", + }); + const err = new ApiError({ message: "500: Server Error", response }); + + fetchCurrentUserMock.mockRejectedValue(err); + + await expect( + getCurrentUser.call(ctx as unknown as DapperTsInterface) + ).rejects.toBe(err); + }); + + it("getCurrentUserTeamPermissions forwards team_name param", async () => { + const ctx = { + config: () => ({ apiHost: "http://api", sessionId: "sid" }), + removeSessionHook: vi.fn(), + } satisfies Pick; + + const fetchPermissionsMock = + fetchCurrentUserTeamPermissions as unknown as Mock; + + fetchPermissionsMock.mockResolvedValue({ ok: true }); + + await expect( + getCurrentUserTeamPermissions.call( + ctx as unknown as DapperTsInterface, + "MyTeam" + ) + ).resolves.toEqual({ ok: true }); + + expect(fetchCurrentUserTeamPermissions).toHaveBeenCalledWith({ + config: ctx.config, + params: { team_name: "MyTeam" }, + data: {}, + queryParams: {}, + }); + }); +}); diff --git a/packages/dapper-ts/src/methods/currentUser.ts b/packages/dapper-ts/src/methods/currentUser.ts index ee80db2f5..1aa4cba2e 100644 --- a/packages/dapper-ts/src/methods/currentUser.ts +++ b/packages/dapper-ts/src/methods/currentUser.ts @@ -17,13 +17,12 @@ export async function getCurrentUser(this: DapperTsInterface) { return data; } catch (error) { if (error instanceof ApiError && error.response.status === 401) { - // If the user is not authenticated, we remove the session hook + // Any 401 means the session/auth context is invalid or stale. + // Clear persisted session data to allow consumers to recover (e.g. re-auth). this.removeSessionHook?.(); return null; - } else { - // If it's another error, we throw it - throw error; } + throw error; } } diff --git a/packages/dapper-ts/src/methods/package.ts b/packages/dapper-ts/src/methods/package.ts index d3e69bec5..59c3a79ca 100644 --- a/packages/dapper-ts/src/methods/package.ts +++ b/packages/dapper-ts/src/methods/package.ts @@ -1,18 +1,19 @@ +import { z } from "zod"; + import { + ApiError, + PackageVersionDependenciesRequestQueryParams, fetchPackageChangelog, + fetchPackagePermissions, fetchPackageReadme, - fetchPackageVersions, - postPackageSubmission, + fetchPackageSource, fetchPackageSubmissionStatus, + fetchPackageVersionDependencies, + fetchPackageVersions, fetchPackageWiki, fetchPackageWikiPage, - fetchPackagePermissions, - fetchPackageSource, - ApiError, - fetchPackageVersionDependencies, - PackageVersionDependenciesRequestQueryParams, + postPackageSubmission, } from "@thunderstore/thunderstore-api"; -import { z } from "zod"; import { DapperTsInterface } from "../index"; diff --git a/packages/dapper-ts/src/methods/packageListings.ts b/packages/dapper-ts/src/methods/packageListings.ts index 95a608986..7cce1cb9b 100644 --- a/packages/dapper-ts/src/methods/packageListings.ts +++ b/packages/dapper-ts/src/methods/packageListings.ts @@ -1,11 +1,11 @@ import { PackageListingType } from "@thunderstore/dapper/types"; import { + PackageListingsOrderingEnum, + PackageListingsRequestQueryParams, fetchCommunityPackageListings, fetchNamespacePackageListings, fetchPackageDependantsListings, fetchPackageListingDetails, - PackageListingsOrderingEnum, - PackageListingsRequestQueryParams, } from "@thunderstore/thunderstore-api"; import { DapperTsInterface } from "../index"; diff --git a/packages/dapper-ts/src/vite-env.d.ts b/packages/dapper-ts/src/vite-env.d.ts new file mode 100644 index 000000000..cd7b9928c --- /dev/null +++ b/packages/dapper-ts/src/vite-env.d.ts @@ -0,0 +1,6 @@ +interface ImportMeta { + readonly env: { + readonly VITE_THUNDERSTORE_TEST_API_HOST?: string; + readonly [key: string]: string | undefined; + }; +} diff --git a/packages/dapper-ts/tsconfig.json b/packages/dapper-ts/tsconfig.json index 509a9bfcb..0b899612a 100644 --- a/packages/dapper-ts/tsconfig.json +++ b/packages/dapper-ts/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, @@ -23,7 +23,7 @@ "outDir": "./dist", "rootDir": "./src", "jsx": "react-jsx", - "types": ["@vitest/browser/providers/playwright"] + "types": ["vite/client", "@vitest/browser/providers/playwright"] }, "include": ["./src/**/*.tsx", "./src/**/*.ts"], "exclude": ["node_modules"] diff --git a/packages/dapper-ts/vitest.config.ts b/packages/dapper-ts/vitest.config.ts index 846f5db08..f2cbfd3fc 100644 --- a/packages/dapper-ts/vitest.config.ts +++ b/packages/dapper-ts/vitest.config.ts @@ -4,10 +4,9 @@ export default defineProject({ test: { include: ["src/**/__tests__/**/*.test.ts"], exclude: ["dist/**/*"], + environment: "node", browser: { - provider: "playwright", - enabled: true, - instances: [{ browser: "chromium", headless: true }], + enabled: false, }, }, }); diff --git a/packages/dapper/package.json b/packages/dapper/package.json index 65716baae..6e610954b 100644 --- a/packages/dapper/package.json +++ b/packages/dapper/package.json @@ -5,6 +5,7 @@ "type": "module", "main": "dist/thunderstore-dapper.cjs.js", "module": "dist/thunderstore-dapper.esm.js", + "types": "dist/index.d.ts", "exports": { ".": { "module": "./dist/thunderstore-dapper.esm.js", diff --git a/packages/dapper/src/context.tsx b/packages/dapper/src/context.tsx index e5992c0e3..987450c2b 100644 --- a/packages/dapper/src/context.tsx +++ b/packages/dapper/src/context.tsx @@ -1,5 +1,6 @@ -import { PropsWithChildren } from "react"; -import { DapperInterface } from "./dapper"; +import type { PropsWithChildren } from "react"; + +import type { DapperInterface } from "./dapper"; import { getDapperContext } from "./singleton"; type DapperProviderProps = PropsWithChildren<{ diff --git a/packages/dapper/src/singleton.ts b/packages/dapper/src/singleton.ts index b46ecedef..66f9b8580 100644 --- a/packages/dapper/src/singleton.ts +++ b/packages/dapper/src/singleton.ts @@ -1,4 +1,4 @@ -import { DapperInterface } from "./dapper"; +import type { DapperInterface } from "./dapper"; interface GlobalContext { Dapper?: DapperContext; diff --git a/packages/dapper/src/types/methods.ts b/packages/dapper/src/types/methods.ts index d27a4ce84..4b0922c92 100644 --- a/packages/dapper/src/types/methods.ts +++ b/packages/dapper/src/types/methods.ts @@ -14,7 +14,7 @@ import { } from "./package"; import { type PackageListingType } from "./props"; import { type HTMLContentResponse, type MarkdownResponse } from "./shared"; -import { type TeamDetails, type ServiceAccount, type TeamMember } from "./team"; +import { type ServiceAccount, type TeamDetails, type TeamMember } from "./team"; import { type CurrentUser, type CurrentUserTeamPermissions } from "./user"; export type GetCommunities = ( diff --git a/packages/dapper/tsconfig.json b/packages/dapper/tsconfig.json index 51e494b37..71137caca 100644 --- a/packages/dapper/tsconfig.json +++ b/packages/dapper/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/graph-system/package.json b/packages/graph-system/package.json index 580033516..29d47265b 100644 --- a/packages/graph-system/package.json +++ b/packages/graph-system/package.json @@ -19,6 +19,7 @@ "test:coverage": "vitest run --coverage" }, "dependencies": { + "@babel/runtime": "^7.25.6", "@thunderstore/thunderstore-api": "^0.1.0", "@thunderstore/typed-event-emitter": "*", "typescript": "^5.6.2" diff --git a/packages/graph-system/src/__tests__/edge.test.ts b/packages/graph-system/src/__tests__/edge.test.ts index 98280ddce..a64feeaf9 100644 --- a/packages/graph-system/src/__tests__/edge.test.ts +++ b/packages/graph-system/src/__tests__/edge.test.ts @@ -1,5 +1,6 @@ +import { describe, expect, it } from "vitest"; + import { GraphEdge, GraphNode } from ".."; -import { describe, it, expect } from "vitest"; describe("GraphEdge", () => { it("should create an edge with source and target nodes", () => { diff --git a/packages/graph-system/src/__tests__/executor.test.ts b/packages/graph-system/src/__tests__/executor.test.ts index 8bb2d6854..a5f3117e8 100644 --- a/packages/graph-system/src/__tests__/executor.test.ts +++ b/packages/graph-system/src/__tests__/executor.test.ts @@ -1,5 +1,6 @@ +import { describe, expect, it, vi } from "vitest"; + import { GraphExecutor, GraphNode } from ".."; -import { vi, describe, it, expect } from "vitest"; describe("GraphExecutor", () => { it("should cover resumeListener branch for shouldExecuteEvent === true", async () => { diff --git a/packages/graph-system/src/__tests__/node.test.ts b/packages/graph-system/src/__tests__/node.test.ts index 5ade1cb89..04d9a4f51 100644 --- a/packages/graph-system/src/__tests__/node.test.ts +++ b/packages/graph-system/src/__tests__/node.test.ts @@ -1,5 +1,6 @@ +import { describe, expect, it } from "vitest"; + import { GraphNode } from ".."; -import { describe, it, expect } from "vitest"; describe("GraphNode", () => { describe("soloLink", () => { diff --git a/packages/graph-system/src/executor.ts b/packages/graph-system/src/executor.ts index ad212ff21..884054eab 100644 --- a/packages/graph-system/src/executor.ts +++ b/packages/graph-system/src/executor.ts @@ -1,6 +1,7 @@ -import { GraphNode } from "."; import { TypedEventEmitter } from "@thunderstore/typed-event-emitter"; +import { GraphNode } from "."; + export class GraphExecutor { private nodes: GraphNode[]; private readonly outputNode: GraphNode, OType>; diff --git a/packages/graph-system/tsconfig.json b/packages/graph-system/tsconfig.json index 509a9bfcb..b9fe180f5 100644 --- a/packages/graph-system/tsconfig.json +++ b/packages/graph-system/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/react-dnd/src/DnDFileInput.tsx b/packages/react-dnd/src/DnDFileInput.tsx index b5f195397..053336a35 100644 --- a/packages/react-dnd/src/DnDFileInput.tsx +++ b/packages/react-dnd/src/DnDFileInput.tsx @@ -1,4 +1,5 @@ -import React, { ReactNode, RefObject, useRef } from "react"; +import { type ReactNode, type RefObject, useRef } from "react"; + import { useDnDFileInput } from "./useDnDFileInput"; interface DnDFileInputProps { @@ -11,7 +12,7 @@ interface DnDFileInputProps { fileInputRef?: RefObject; } -export const DnDFileInput: React.FC = (props) => { +export const DnDFileInput = (props: DnDFileInputProps) => { const fileInputRef = props.fileInputRef ?? useRef(null); const { onChange, onDrop, isDragging } = useDnDFileInput({ inputRef: fileInputRef, diff --git a/packages/react-dnd/src/useDnDFileInput.ts b/packages/react-dnd/src/useDnDFileInput.ts index 9d42ca93a..f0e83745a 100644 --- a/packages/react-dnd/src/useDnDFileInput.ts +++ b/packages/react-dnd/src/useDnDFileInput.ts @@ -1,4 +1,5 @@ -import React, { RefObject } from "react"; +import type { DragEvent, RefObject } from "react"; + import { useDnD } from "./useDnD"; type useDragAndDropInputProps = { @@ -20,7 +21,7 @@ export const useDnDFileInput = (props: useDragAndDropInputProps) => { resetDrag(); }; - const onDrop = (e: React.DragEvent) => { + const onDrop = (e: DragEvent) => { if (!props.readonly) { const inp = props.inputRef?.current; if (inp) { diff --git a/packages/react-dnd/tsconfig.json b/packages/react-dnd/tsconfig.json index dfe40575b..b2dec1493 100644 --- a/packages/react-dnd/tsconfig.json +++ b/packages/react-dnd/tsconfig.json @@ -3,7 +3,7 @@ "target": "es5", "module": "ES2020", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/thunderstore-api/package.json b/packages/thunderstore-api/package.json index 9b9a5d595..963bdbedb 100644 --- a/packages/thunderstore-api/package.json +++ b/packages/thunderstore-api/package.json @@ -4,9 +4,11 @@ "description": "Thunderstore API client", "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/packages/thunderstore-api", "main": "dist/thunderstore-thunderstore-api.cjs.js", + "types": "dist/index.d.ts", "module": "dist/thunderstore-thunderstore-api.esm.js", "exports": { ".": { + "types": "./dist/index.d.ts", "module": "./dist/thunderstore-thunderstore-api.esm.js", "default": "./dist/thunderstore-thunderstore-api.cjs.js" }, @@ -22,12 +24,14 @@ "watch": "vitest watch" }, "dependencies": { - "@babel/runtime": "^7.25.6" + "@babel/runtime": "^7.25.6", + "zod": "^3.23.8" }, "devDependencies": { "@vitest/browser": "3.2.4", "playwright": "1.55.1", "typescript": "^5.6.2", + "vite": "7.1.7", "vitest": "3.2.4", "zod": "^3.23.8" }, diff --git a/packages/thunderstore-api/src/__tests__/defaultConfig.ts b/packages/thunderstore-api/src/__tests__/defaultConfig.ts index 9f665916c..17400fffd 100644 --- a/packages/thunderstore-api/src/__tests__/defaultConfig.ts +++ b/packages/thunderstore-api/src/__tests__/defaultConfig.ts @@ -1,6 +1,8 @@ export const config = () => { return { - apiHost: "http://127.0.0.1:8000", + apiHost: + import.meta.env.VITE_THUNDERSTORE_TEST_API_HOST ?? + "http://127.0.0.1:8000", }; }; diff --git a/packages/thunderstore-api/src/__tests__/queryString.test.ts b/packages/thunderstore-api/src/__tests__/queryString.test.ts index d97990efa..5525037b2 100644 --- a/packages/thunderstore-api/src/__tests__/queryString.test.ts +++ b/packages/thunderstore-api/src/__tests__/queryString.test.ts @@ -1,4 +1,5 @@ -import { describe, it, expect } from "vitest"; +import { describe, expect, it } from "vitest"; + import { arraysAreEqual, serializeQueryString } from "../queryString"; describe("serializeQueryString", () => { diff --git a/packages/thunderstore-api/src/__tests__/typeguards.test.ts b/packages/thunderstore-api/src/__tests__/typeguards.test.ts index d86d07531..9468ff76a 100644 --- a/packages/thunderstore-api/src/__tests__/typeguards.test.ts +++ b/packages/thunderstore-api/src/__tests__/typeguards.test.ts @@ -1,4 +1,5 @@ -import { describe, it, expect } from "vitest"; +import { describe, expect, it } from "vitest"; + import { isNumberArray, isStringArray } from "../typeguards"; describe("isNumberArray", () => { diff --git a/packages/thunderstore-api/src/apiFetch.ts b/packages/thunderstore-api/src/apiFetch.ts index 634c7d7ce..8933b8e2e 100644 --- a/packages/thunderstore-api/src/apiFetch.ts +++ b/packages/thunderstore-api/src/apiFetch.ts @@ -1,11 +1,12 @@ +import { z } from "zod"; + +import type { RequestConfig } from "./index"; import { ApiError, ParseError, - RequestConfig, RequestBodyParseError, RequestQueryParamsParseError, } from "./index"; -import { z } from "zod"; import { serializeQueryString } from "./queryString"; const BASE_HEADERS = { diff --git a/packages/thunderstore-api/src/delete/packageWiki.ts b/packages/thunderstore-api/src/delete/packageWiki.ts index 666c55bca..672579dc0 100644 --- a/packages/thunderstore-api/src/delete/packageWiki.ts +++ b/packages/thunderstore-api/src/delete/packageWiki.ts @@ -1,9 +1,9 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - PackageWikiPageDeleteRequestData, + type PackageWikiPageDeleteRequestData, + type PackageWikiPageDeleteRequestParams, packageWikiPageDeleteRequestDataSchema, - PackageWikiPageDeleteRequestParams, } from "../schemas/requestSchemas"; export async function deletePackageWikiPage( diff --git a/packages/thunderstore-api/src/delete/teamDisband.ts b/packages/thunderstore-api/src/delete/teamDisband.ts index 968c1e23e..e97c583dc 100644 --- a/packages/thunderstore-api/src/delete/teamDisband.ts +++ b/packages/thunderstore-api/src/delete/teamDisband.ts @@ -1,6 +1,6 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; -import { TeamDisbandRequestParams } from "../schemas/requestSchemas"; +import type { ApiEndpointProps } from "../index"; +import type { TeamDisbandRequestParams } from "../schemas/requestSchemas"; export function teamDisband( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/delete/teamRemoveMember.ts b/packages/thunderstore-api/src/delete/teamRemoveMember.ts index 10e6aad0c..e41a50ad9 100644 --- a/packages/thunderstore-api/src/delete/teamRemoveMember.ts +++ b/packages/thunderstore-api/src/delete/teamRemoveMember.ts @@ -1,5 +1,5 @@ -import { ApiEndpointProps, TeamMemberRemoveRequestParams } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps, TeamMemberRemoveRequestParams } from "../index"; export function teamRemoveMember( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/delete/teamServiceAccountRemove.ts b/packages/thunderstore-api/src/delete/teamServiceAccountRemove.ts index e4cf93ce0..966838a34 100644 --- a/packages/thunderstore-api/src/delete/teamServiceAccountRemove.ts +++ b/packages/thunderstore-api/src/delete/teamServiceAccountRemove.ts @@ -1,8 +1,8 @@ -import { +import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps, TeamServiceAccountRemoveRequestParams, } from "../index"; -import { apiFetch } from "../apiFetch"; export function teamServiceAccountRemove( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/delete/userDelete.ts b/packages/thunderstore-api/src/delete/userDelete.ts index 0c3904e32..11cea895e 100644 --- a/packages/thunderstore-api/src/delete/userDelete.ts +++ b/packages/thunderstore-api/src/delete/userDelete.ts @@ -1,5 +1,5 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; export function userDelete( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/delete/userLinkedAccountDisconnect.ts b/packages/thunderstore-api/src/delete/userLinkedAccountDisconnect.ts index ea3b21d0a..525f154f1 100644 --- a/packages/thunderstore-api/src/delete/userLinkedAccountDisconnect.ts +++ b/packages/thunderstore-api/src/delete/userLinkedAccountDisconnect.ts @@ -1,9 +1,9 @@ -import { +import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps, UserLinkedAccountDisconnectRequestParams, - userLinkedAccountDisconnectRequestDataSchema, } from "../index"; -import { apiFetch } from "../apiFetch"; +import { userLinkedAccountDisconnectRequestDataSchema } from "../index"; export function userLinkedAccountDisconnect( props: ApiEndpointProps< diff --git a/packages/thunderstore-api/src/get/__tests__/community.test.ts b/packages/thunderstore-api/src/get/__tests__/community.test.ts index 513846187..7cc29160c 100644 --- a/packages/thunderstore-api/src/get/__tests__/community.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/community.test.ts @@ -1,4 +1,5 @@ import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchCommunity } from "../community"; diff --git a/packages/thunderstore-api/src/get/__tests__/communityFilters.test.ts b/packages/thunderstore-api/src/get/__tests__/communityFilters.test.ts index b92ea307a..7e72b584e 100644 --- a/packages/thunderstore-api/src/get/__tests__/communityFilters.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/communityFilters.test.ts @@ -1,4 +1,5 @@ -import { it, expect } from "vitest"; +import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchCommunityFilters } from "../communityFilters"; diff --git a/packages/thunderstore-api/src/get/__tests__/communityList.test.ts b/packages/thunderstore-api/src/get/__tests__/communityList.test.ts index f137bc7de..b7026a3a2 100644 --- a/packages/thunderstore-api/src/get/__tests__/communityList.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/communityList.test.ts @@ -1,7 +1,8 @@ +import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; -import { fetchCommunityList } from "../communityList"; import { CommunityListOrderingEnum } from "../../schemas/queryParamSchemas"; -import { it, expect } from "vitest"; +import { fetchCommunityList } from "../communityList"; it("finds a community in the community listing", async () => { const response = await fetchCommunityList({ diff --git a/packages/thunderstore-api/src/get/__tests__/communityPackageListings.test.ts b/packages/thunderstore-api/src/get/__tests__/communityPackageListings.test.ts index cf3b12674..9e5f84656 100644 --- a/packages/thunderstore-api/src/get/__tests__/communityPackageListings.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/communityPackageListings.test.ts @@ -1,7 +1,8 @@ +import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; -import { fetchCommunityPackageListings } from "../communityPackageListings"; import { PackageListingsOrderingEnum } from "../../schemas/queryParamSchemas"; -import { it, expect } from "vitest"; +import { fetchCommunityPackageListings } from "../communityPackageListings"; interface PartialPackage { community_identifier: string; diff --git a/packages/thunderstore-api/src/get/__tests__/currentUser.test.ts b/packages/thunderstore-api/src/get/__tests__/currentUser.test.ts index ab250361e..d8f400407 100644 --- a/packages/thunderstore-api/src/get/__tests__/currentUser.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/currentUser.test.ts @@ -1,4 +1,5 @@ -import { it, expect } from "vitest"; +import { expect, it } from "vitest"; + import { config } from "../../__tests__/defaultConfig"; import { fetchCurrentUser } from "../currentUser"; diff --git a/packages/thunderstore-api/src/get/__tests__/namespacePackageListings.test.ts b/packages/thunderstore-api/src/get/__tests__/namespacePackageListings.test.ts index 2b5b0ba1b..6f5548969 100644 --- a/packages/thunderstore-api/src/get/__tests__/namespacePackageListings.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/namespacePackageListings.test.ts @@ -1,7 +1,8 @@ +import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; -import { fetchNamespacePackageListings } from "../namespacePackageListings"; import { PackageListingsOrderingEnum } from "../../schemas/queryParamSchemas"; -import { it, expect } from "vitest"; +import { fetchNamespacePackageListings } from "../namespacePackageListings"; interface PartialPackage { community_identifier: string; diff --git a/packages/thunderstore-api/src/get/__tests__/packageChangelog.test.ts b/packages/thunderstore-api/src/get/__tests__/packageChangelog.test.ts index 4723f7fb7..55b90b27e 100644 --- a/packages/thunderstore-api/src/get/__tests__/packageChangelog.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/packageChangelog.test.ts @@ -1,4 +1,5 @@ import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchPackageChangelog } from "../packageChangelog"; diff --git a/packages/thunderstore-api/src/get/__tests__/packageDependantsListings.test.ts b/packages/thunderstore-api/src/get/__tests__/packageDependantsListings.test.ts index 661128588..e1bce800e 100644 --- a/packages/thunderstore-api/src/get/__tests__/packageDependantsListings.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/packageDependantsListings.test.ts @@ -1,7 +1,8 @@ +import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; -import { fetchPackageDependantsListings } from "../packageDependantsListings"; import { PackageListingsOrderingEnum } from "../../schemas/queryParamSchemas"; -import { it, expect } from "vitest"; +import { fetchPackageDependantsListings } from "../packageDependantsListings"; interface PartialPackage { community_identifier: string; diff --git a/packages/thunderstore-api/src/get/__tests__/packageListingDetails.test.ts b/packages/thunderstore-api/src/get/__tests__/packageListingDetails.test.ts index c74508f5b..48e8aab87 100644 --- a/packages/thunderstore-api/src/get/__tests__/packageListingDetails.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/packageListingDetails.test.ts @@ -1,4 +1,5 @@ -import { it, expect } from "vitest"; +import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchPackageListingDetails } from "../packageListingDetails"; diff --git a/packages/thunderstore-api/src/get/__tests__/packageReadme.test.ts b/packages/thunderstore-api/src/get/__tests__/packageReadme.test.ts index 952a171c5..85f0cc202 100644 --- a/packages/thunderstore-api/src/get/__tests__/packageReadme.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/packageReadme.test.ts @@ -1,4 +1,5 @@ import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchPackageReadme } from "../packageReadme"; diff --git a/packages/thunderstore-api/src/get/__tests__/packageSource.test.ts b/packages/thunderstore-api/src/get/__tests__/packageSource.test.ts index 44f653b72..78396a68f 100644 --- a/packages/thunderstore-api/src/get/__tests__/packageSource.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/packageSource.test.ts @@ -1,25 +1,32 @@ import { expect, test } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; -import { fetchPackageSource } from "../packageSource"; import { packageSourceResponseDataSchema } from "../../schemas/responseSchemas"; +import { fetchPackageSource } from "../packageSource"; -// TODO: Disabled temporarily until we decide on a testing strategy/policy regarding e2e tests -test.skip("ensures package source can be fetched", async () => { +test("ensures package source endpoint works when enabled (or 404 when disabled)", async () => { const { namespaceId, packageName } = testData; - const response = await fetchPackageSource({ - config, - params: { - namespace_id: namespaceId, - package_name: packageName, - }, - data: {}, - queryParams: {}, - }); - expect(response.is_visible).toBe(true); - expect(response.namespace).toBe(namespaceId); - expect(response.package_name).toBe(packageName); - expect(response.last_decompilation_date).toBeDefined(); - expect(response.decompilations.length).toBeGreaterThan(0); - expect(packageSourceResponseDataSchema.parse(response)).toEqual(response); + try { + const response = await fetchPackageSource({ + config, + params: { + namespace_id: namespaceId, + package_name: packageName, + }, + data: {}, + queryParams: {}, + }); + + expect(packageSourceResponseDataSchema.parse(response)).toEqual(response); + expect(Array.isArray(response.decompilations)).toBe(true); + } catch (err) { + // The container test backend may not include the plugin that registers this endpoint. + // Treat a 404 as "endpoint disabled". + if (err instanceof Error && err.message.includes("404")) { + return; + } + + throw err; + } }); diff --git a/packages/thunderstore-api/src/get/__tests__/packageVersionDependencies.test.ts b/packages/thunderstore-api/src/get/__tests__/packageVersionDependencies.test.ts index 12b3f78f2..59e297a07 100644 --- a/packages/thunderstore-api/src/get/__tests__/packageVersionDependencies.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/packageVersionDependencies.test.ts @@ -1,10 +1,10 @@ import { expect, test } from "vitest"; + +import { packageVersionDependenciesResponseDataSchema } from "../.."; import { config, testData } from "../../__tests__/defaultConfig"; import { fetchPackageVersionDependencies } from "../packageVersionDependencies"; -import { packageVersionDependenciesResponseDataSchema } from "../.."; -// TODO: Disabled temporarily until we decide on a testing strategy/policy regarding e2e tests -test.skip("ensures package version dependencies can be fetched", async () => { +test("ensures package version dependencies can be fetched", async () => { const { namespaceId, packageName, versionNumber } = testData; const response = await fetchPackageVersionDependencies({ config, @@ -17,9 +17,9 @@ test.skip("ensures package version dependencies can be fetched", async () => { queryParams: [{ key: "page", value: 1, impotent: 1 }], }); - expect(response.count).toBe(3); - expect(response.results.length).toBe(3); expect(packageVersionDependenciesResponseDataSchema.parse(response)).toEqual( response ); + expect(response.count).toBeTypeOf("number"); + expect(Array.isArray(response.results)).toBe(true); }); diff --git a/packages/thunderstore-api/src/get/__tests__/packageVersions.test.ts b/packages/thunderstore-api/src/get/__tests__/packageVersions.test.ts index 5225a2981..807bb665b 100644 --- a/packages/thunderstore-api/src/get/__tests__/packageVersions.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/packageVersions.test.ts @@ -1,4 +1,5 @@ import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchPackageVersions } from "../packageVersions"; diff --git a/packages/thunderstore-api/src/get/__tests__/teamDetails.test.ts b/packages/thunderstore-api/src/get/__tests__/teamDetails.test.ts index f2ba011a9..820d672f1 100644 --- a/packages/thunderstore-api/src/get/__tests__/teamDetails.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/teamDetails.test.ts @@ -1,4 +1,5 @@ -import { it, expect } from "vitest"; +import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchTeamDetails } from "../teamDetails"; diff --git a/packages/thunderstore-api/src/get/__tests__/teamMembers.test.ts b/packages/thunderstore-api/src/get/__tests__/teamMembers.test.ts index cb0db29dc..f41d3bce9 100644 --- a/packages/thunderstore-api/src/get/__tests__/teamMembers.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/teamMembers.test.ts @@ -1,4 +1,5 @@ import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchTeamMembers } from "../teamMembers"; diff --git a/packages/thunderstore-api/src/get/__tests__/teamServiceAccounts.test.ts b/packages/thunderstore-api/src/get/__tests__/teamServiceAccounts.test.ts index ddb4ee9ca..2308fdf71 100644 --- a/packages/thunderstore-api/src/get/__tests__/teamServiceAccounts.test.ts +++ b/packages/thunderstore-api/src/get/__tests__/teamServiceAccounts.test.ts @@ -1,4 +1,5 @@ import { expect, it } from "vitest"; + import { config, testData } from "../../__tests__/defaultConfig"; import { fetchTeamServiceAccounts } from "../teamServiceAccounts"; diff --git a/packages/thunderstore-api/src/get/community.ts b/packages/thunderstore-api/src/get/community.ts index e532f0d81..402e879a7 100644 --- a/packages/thunderstore-api/src/get/community.ts +++ b/packages/thunderstore-api/src/get/community.ts @@ -1,10 +1,10 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import type { CommunityRequestParams } from "../schemas/requestSchemas"; import { + type CommunityResponseData, communityResponseDataSchema, - CommunityResponseData, } from "../schemas/responseSchemas"; -import { CommunityRequestParams } from "../schemas/requestSchemas"; export async function fetchCommunity( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/get/communityFilters.ts b/packages/thunderstore-api/src/get/communityFilters.ts index cf3814dde..6ec21b71b 100644 --- a/packages/thunderstore-api/src/get/communityFilters.ts +++ b/packages/thunderstore-api/src/get/communityFilters.ts @@ -1,12 +1,12 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - CommunityFiltersRequestParams, + type CommunityFiltersRequestParams, communityFiltersRequestParamsSchema, } from "../schemas/requestSchemas"; import { + type CommunityFiltersResponseData, communityFiltersResponseDataSchema, - CommunityFiltersResponseData, } from "../schemas/responseSchemas"; export async function fetchCommunityFilters( diff --git a/packages/thunderstore-api/src/get/communityList.ts b/packages/thunderstore-api/src/get/communityList.ts index 8e18fa7f3..f29ffe74e 100644 --- a/packages/thunderstore-api/src/get/communityList.ts +++ b/packages/thunderstore-api/src/get/communityList.ts @@ -1,13 +1,13 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import { CommunityListOrderingEnum } from "../schemas/queryParamSchemas"; import { - CommunityListRequestQueryParams, + type CommunityListRequestQueryParams, communityListRequestQueryParamsSchema, } from "../schemas/requestSchemas"; -import { CommunityListOrderingEnum } from "../schemas/queryParamSchemas"; import { + type CommunityListResponseData, communityListResponseDataSchema, - CommunityListResponseData, } from "../schemas/responseSchemas"; export async function fetchCommunityList( diff --git a/packages/thunderstore-api/src/get/communityPackageListings.ts b/packages/thunderstore-api/src/get/communityPackageListings.ts index 31c00999a..a1b1c59c6 100644 --- a/packages/thunderstore-api/src/get/communityPackageListings.ts +++ b/packages/thunderstore-api/src/get/communityPackageListings.ts @@ -1,14 +1,14 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import { PackageListingsOrderingEnum } from "../schemas/queryParamSchemas"; import { + type CommunityPackageListingsRequestParams, + type PackageListingsRequestQueryParams, communityPackageListingsRequestParamsSchema, - CommunityPackageListingsRequestParams, packageListingsRequestQueryParamsSchema, - PackageListingsRequestQueryParams, } from "../schemas/requestSchemas"; -import { PackageListingsOrderingEnum } from "../schemas/queryParamSchemas"; import { - PackageListingsResponseData, + type PackageListingsResponseData, packageListingsResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/get/currentUser.ts b/packages/thunderstore-api/src/get/currentUser.ts index 95e043fa6..62bf5947a 100644 --- a/packages/thunderstore-api/src/get/currentUser.ts +++ b/packages/thunderstore-api/src/get/currentUser.ts @@ -1,13 +1,13 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { type CurrentUserTeamPermissionsRequestParams, currentUserTeamPermissionsRequestParamsSchema, } from "../schemas/requestSchemas"; import { - CurrentUserResponseData, + type CurrentUserResponseData, + type CurrentUserTeamPermissionsResponseData, currentUserResponseDataSchema, - CurrentUserTeamPermissionsResponseData, currentUserTeamPermissionsResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/get/dynamicHTML.ts b/packages/thunderstore-api/src/get/dynamicHTML.ts index 318337408..403d64261 100644 --- a/packages/thunderstore-api/src/get/dynamicHTML.ts +++ b/packages/thunderstore-api/src/get/dynamicHTML.ts @@ -1,10 +1,10 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import type { DynamicHTMLRequestParams } from "../schemas/requestSchemas"; import { - DynamicHTMLResponseData, + type DynamicHTMLResponseData, dynamicHTMLResponseDataSchema, } from "../schemas/responseSchemas"; -import { DynamicHTMLRequestParams } from "../schemas/requestSchemas"; export async function fetchDynamicHTML( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/get/namespacePackageListings.ts b/packages/thunderstore-api/src/get/namespacePackageListings.ts index a5f5201eb..006f0ec18 100644 --- a/packages/thunderstore-api/src/get/namespacePackageListings.ts +++ b/packages/thunderstore-api/src/get/namespacePackageListings.ts @@ -1,15 +1,15 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import { PackageListingsOrderingEnum } from "../schemas/queryParamSchemas"; import { - NamespacePackageListingsRequestParams, - PackageListingsRequestQueryParams, + type NamespacePackageListingsRequestParams, + type PackageListingsRequestQueryParams, packageListingsRequestQueryParamsSchema, } from "../schemas/requestSchemas"; import { - PackageListingsResponseData, + type PackageListingsResponseData, packageListingsResponseDataSchema, } from "../schemas/responseSchemas"; -import { PackageListingsOrderingEnum } from "../schemas/queryParamSchemas"; export async function fetchNamespacePackageListings( props: ApiEndpointProps< diff --git a/packages/thunderstore-api/src/get/package.ts b/packages/thunderstore-api/src/get/package.ts index 1076c033e..396decdc2 100644 --- a/packages/thunderstore-api/src/get/package.ts +++ b/packages/thunderstore-api/src/get/package.ts @@ -1,8 +1,8 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; -import { PackagePermissionsRequestParams } from "../schemas/requestSchemas"; +import type { ApiEndpointProps } from "../index"; +import type { PackagePermissionsRequestParams } from "../schemas/requestSchemas"; import { - PackagePermissionsResponseData, + type PackagePermissionsResponseData, packagePermissionsResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/get/packageChangelog.ts b/packages/thunderstore-api/src/get/packageChangelog.ts index c44543aab..f3dbc621d 100644 --- a/packages/thunderstore-api/src/get/packageChangelog.ts +++ b/packages/thunderstore-api/src/get/packageChangelog.ts @@ -1,8 +1,8 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; -import { PackageChangelogRequestParams } from "../schemas/requestSchemas"; +import type { ApiEndpointProps } from "../index"; +import type { PackageChangelogRequestParams } from "../schemas/requestSchemas"; import { - PackageChangelogResponseData, + type PackageChangelogResponseData, packageChangelogResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/get/packageDependantsListings.ts b/packages/thunderstore-api/src/get/packageDependantsListings.ts index e51e220a5..3e47c369a 100644 --- a/packages/thunderstore-api/src/get/packageDependantsListings.ts +++ b/packages/thunderstore-api/src/get/packageDependantsListings.ts @@ -1,13 +1,13 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import { PackageListingsOrderingEnum } from "../schemas/queryParamSchemas"; import { - PackageDependantsListingsRequestParams, - PackageListingsRequestQueryParams, + type PackageDependantsListingsRequestParams, + type PackageListingsRequestQueryParams, packageListingsRequestQueryParamsSchema, } from "../schemas/requestSchemas"; -import { PackageListingsOrderingEnum } from "../schemas/queryParamSchemas"; import { - PackageListingsResponseData, + type PackageListingsResponseData, packageListingsResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/get/packageListingDetails.ts b/packages/thunderstore-api/src/get/packageListingDetails.ts index 9c98c3dc8..0455d0d55 100644 --- a/packages/thunderstore-api/src/get/packageListingDetails.ts +++ b/packages/thunderstore-api/src/get/packageListingDetails.ts @@ -1,8 +1,8 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { packageListingDetailsSchema } from "../schemas/objectSchemas"; -import { PackageListingDetailsRequestParams } from "../schemas/requestSchemas"; -import { PackageListingDetailsResponseData } from "../schemas/responseSchemas"; +import type { PackageListingDetailsRequestParams } from "../schemas/requestSchemas"; +import type { PackageListingDetailsResponseData } from "../schemas/responseSchemas"; export async function fetchPackageListingDetails( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/get/packageReadme.ts b/packages/thunderstore-api/src/get/packageReadme.ts index fedd07b23..5ba59674e 100644 --- a/packages/thunderstore-api/src/get/packageReadme.ts +++ b/packages/thunderstore-api/src/get/packageReadme.ts @@ -1,10 +1,11 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import type { PackageReadmeRequestParams } from "../schemas/requestSchemas"; import { - PackageReadmeResponseData, + type PackageReadmeResponseData, packageReadmeResponseDataSchema, } from "../schemas/responseSchemas"; -import { PackageReadmeRequestParams } from "../schemas/requestSchemas"; + export async function fetchPackageReadme( props: ApiEndpointProps ): Promise { diff --git a/packages/thunderstore-api/src/get/packageSource.ts b/packages/thunderstore-api/src/get/packageSource.ts index 334778407..adde13258 100644 --- a/packages/thunderstore-api/src/get/packageSource.ts +++ b/packages/thunderstore-api/src/get/packageSource.ts @@ -1,8 +1,8 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import type { PackageSourceRequestParams } from "../schemas/requestSchemas"; import { packageSourceResponseDataSchema } from "../schemas/responseSchemas"; -import { PackageSourceRequestParams } from "../schemas/requestSchemas"; -import { PackageSourceResponseData } from "../schemas/responseSchemas"; +import type { PackageSourceResponseData } from "../schemas/responseSchemas"; export async function fetchPackageSource( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/get/packageSubmission.ts b/packages/thunderstore-api/src/get/packageSubmission.ts index d5705fcea..33bbe4fc5 100644 --- a/packages/thunderstore-api/src/get/packageSubmission.ts +++ b/packages/thunderstore-api/src/get/packageSubmission.ts @@ -1,8 +1,8 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; -import { PackageSubmissionStatusRequestParams } from "../schemas/requestSchemas"; +import type { ApiEndpointProps } from "../index"; +import type { PackageSubmissionStatusRequestParams } from "../schemas/requestSchemas"; import { - PackageSubmissionStatusResponseData, + type PackageSubmissionStatusResponseData, packageSubmissionStatusResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/get/packageVersionDependencies.ts b/packages/thunderstore-api/src/get/packageVersionDependencies.ts index 2cd913d9c..7a33fa5a9 100644 --- a/packages/thunderstore-api/src/get/packageVersionDependencies.ts +++ b/packages/thunderstore-api/src/get/packageVersionDependencies.ts @@ -1,12 +1,14 @@ -import { +import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps, - packageVersionDependenciesRequestQueryParamsSchema, - PackageVersionDependenciesRequestQueryParams, PackageVersionDependenciesRequestParams, - packageVersionDependenciesResponseDataSchema, + PackageVersionDependenciesRequestQueryParams, PackageVersionDependenciesResponseData, } from "../index"; -import { apiFetch } from "../apiFetch"; +import { + packageVersionDependenciesRequestQueryParamsSchema, + packageVersionDependenciesResponseDataSchema, +} from "../index"; export async function fetchPackageVersionDependencies( props: ApiEndpointProps< diff --git a/packages/thunderstore-api/src/get/packageVersionDetails.ts b/packages/thunderstore-api/src/get/packageVersionDetails.ts index 44e57b238..8b7ece2e9 100644 --- a/packages/thunderstore-api/src/get/packageVersionDetails.ts +++ b/packages/thunderstore-api/src/get/packageVersionDetails.ts @@ -1,8 +1,8 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; -import { PackageVersionDetailsRequestParams } from "../schemas/requestSchemas"; +import type { ApiEndpointProps } from "../index"; +import type { PackageVersionDetailsRequestParams } from "../schemas/requestSchemas"; import { - PackageVersionDetailsResponseData, + type PackageVersionDetailsResponseData, packageVersionDetailsResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/get/packageVersions.ts b/packages/thunderstore-api/src/get/packageVersions.ts index 291cd8eaf..4f01cad48 100644 --- a/packages/thunderstore-api/src/get/packageVersions.ts +++ b/packages/thunderstore-api/src/get/packageVersions.ts @@ -1,8 +1,8 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import type { PackageVersionsRequestParams } from "../schemas/requestSchemas"; import { packageVersionsResponseDataSchema } from "../schemas/responseSchemas"; -import { PackageVersionsRequestParams } from "../schemas/requestSchemas"; -import { PackageVersionsResponseData } from "../schemas/responseSchemas"; +import type { PackageVersionsResponseData } from "../schemas/responseSchemas"; export async function fetchPackageVersions( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/get/packageWiki.ts b/packages/thunderstore-api/src/get/packageWiki.ts index 6685a10e7..e8218d3c3 100644 --- a/packages/thunderstore-api/src/get/packageWiki.ts +++ b/packages/thunderstore-api/src/get/packageWiki.ts @@ -1,13 +1,13 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - PackageWikiPageRequestParams, - PackageWikiRequestParams, + type PackageWikiPageRequestParams, + type PackageWikiRequestParams, } from "../schemas/requestSchemas"; import { - PackageWikiPageResponseData, + type PackageWikiPageResponseData, + type PackageWikiResponseData, packageWikiPageResponseDataSchema, - PackageWikiResponseData, packageWikiResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/get/ratedPackages.ts b/packages/thunderstore-api/src/get/ratedPackages.ts index eb5cb7b36..d88b85fb2 100644 --- a/packages/thunderstore-api/src/get/ratedPackages.ts +++ b/packages/thunderstore-api/src/get/ratedPackages.ts @@ -1,7 +1,7 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { ratedPackagesResponseDataSchema } from "../schemas/responseSchemas"; -import { RatedPackagesResponseData } from "../schemas/responseSchemas"; +import type { RatedPackagesResponseData } from "../schemas/responseSchemas"; export async function fetchRatedPackages( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/get/teamDetails.ts b/packages/thunderstore-api/src/get/teamDetails.ts index ef3b1e3a3..b1c381fd5 100644 --- a/packages/thunderstore-api/src/get/teamDetails.ts +++ b/packages/thunderstore-api/src/get/teamDetails.ts @@ -1,10 +1,10 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import type { TeamDetailsRequestParams } from "../schemas/requestSchemas"; import { - TeamDetailsResponseData, + type TeamDetailsResponseData, teamDetailsResponseDataSchema, } from "../schemas/responseSchemas"; -import { TeamDetailsRequestParams } from "../schemas/requestSchemas"; export async function fetchTeamDetails( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/get/teamMembers.ts b/packages/thunderstore-api/src/get/teamMembers.ts index 02ae9ff88..838ac2a11 100644 --- a/packages/thunderstore-api/src/get/teamMembers.ts +++ b/packages/thunderstore-api/src/get/teamMembers.ts @@ -1,10 +1,10 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; +import type { TeamMembersRequestParams } from "../schemas/requestSchemas"; import { - TeamMembersResponseData, + type TeamMembersResponseData, teamMembersResponseDataSchema, } from "../schemas/responseSchemas"; -import { TeamMembersRequestParams } from "../schemas/requestSchemas"; export async function fetchTeamMembers( props: ApiEndpointProps diff --git a/packages/thunderstore-api/src/get/teamServiceAccounts.ts b/packages/thunderstore-api/src/get/teamServiceAccounts.ts index d1dc7e63c..b2f8735d4 100644 --- a/packages/thunderstore-api/src/get/teamServiceAccounts.ts +++ b/packages/thunderstore-api/src/get/teamServiceAccounts.ts @@ -1,8 +1,8 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; -import { TeamServiceAccountsRequestParams } from "../schemas/requestSchemas"; +import type { ApiEndpointProps } from "../index"; +import type { TeamServiceAccountsRequestParams } from "../schemas/requestSchemas"; import { - TeamServiceAccountsResponseData, + type TeamServiceAccountsResponseData, teamServiceAccountsResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/patch/teamDetailsEdit.ts b/packages/thunderstore-api/src/patch/teamDetailsEdit.ts index f51c13de8..d91932d77 100644 --- a/packages/thunderstore-api/src/patch/teamDetailsEdit.ts +++ b/packages/thunderstore-api/src/patch/teamDetailsEdit.ts @@ -1,12 +1,14 @@ -import { +import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps, TeamDetailsEditRequestData, - teamDetailsEditRequestDataSchema, TeamDetailsEditRequestParams, TeamDetailsEditResponseData, +} from "../index"; +import { + teamDetailsEditRequestDataSchema, teamDetailsEditResponseSchema, } from "../index"; -import { apiFetch } from "../apiFetch"; export type teamDetailsEditMetaArgs = { teamIdentifier: string; diff --git a/packages/thunderstore-api/src/patch/teamEditMember.ts b/packages/thunderstore-api/src/patch/teamEditMember.ts index 55962b98e..c1ee5b427 100644 --- a/packages/thunderstore-api/src/patch/teamEditMember.ts +++ b/packages/thunderstore-api/src/patch/teamEditMember.ts @@ -1,11 +1,13 @@ -import { +import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps, - teamEditMemberResponseSchema, TeamMemberEditRequestData, TeamMemberEditRequestParams, +} from "../index"; +import { + teamEditMemberResponseSchema, teamMemberEditRequestParamsSchema, } from "../index"; -import { apiFetch } from "../apiFetch"; export type teamEditMemberMetaArgs = { teamIdentifier: string; diff --git a/packages/thunderstore-api/src/post/frontend.ts b/packages/thunderstore-api/src/post/frontend.ts index aa601ffea..9345c2929 100644 --- a/packages/thunderstore-api/src/post/frontend.ts +++ b/packages/thunderstore-api/src/post/frontend.ts @@ -1,11 +1,11 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - MarkdownRenderRequestData, + type MarkdownRenderRequestData, markdownRenderRequestDataSchema, } from "../schemas/requestSchemas"; import { - MarkdownRenderResponseData, + type MarkdownRenderResponseData, markdownRenderResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/post/package.ts b/packages/thunderstore-api/src/post/package.ts index 675bea08c..9d7d14ef2 100644 --- a/packages/thunderstore-api/src/post/package.ts +++ b/packages/thunderstore-api/src/post/package.ts @@ -1,22 +1,24 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - PackageDeprecateRequestData, + type PackageDeprecateRequestData, + type PackageDeprecateRequestParams, + type PackageUnlistRequestData, + type PackageUnlistRequestParams, packageDeprecateRequestDataSchema, - PackageDeprecateRequestParams, packageRateRequestDataSchema, - PackageUnlistRequestData, packageUnlistRequestDataSchema, - PackageUnlistRequestParams, } from "../schemas/requestSchemas"; -import { PackageRateRequestData } from "../schemas/requestSchemas"; -import { PackageRateRequestParams } from "../schemas/requestSchemas"; +import type { + PackageRateRequestData, + PackageRateRequestParams, +} from "../schemas/requestSchemas"; import { - PackageDeprecateResponseData, + type PackageDeprecateResponseData, + type PackageRateResponseData, + type PackageUnlistResponseData, packageDeprecateResponseDataSchema, packageRateResponseDataSchema, - PackageRateResponseData, - PackageUnlistResponseData, packageUnlistResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/post/packageListing.ts b/packages/thunderstore-api/src/post/packageListing.ts index cfcf73116..7acecdb51 100644 --- a/packages/thunderstore-api/src/post/packageListing.ts +++ b/packages/thunderstore-api/src/post/packageListing.ts @@ -1,21 +1,21 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - PackageListingApproveRequestData, - PackageListingApproveRequestParams, - PackageListingRejectRequestData, - PackageListingRejectRequestParams, - PackageListingUpdateRequestData, - PackageListingUpdateRequestParams, - packageListingUpdateRequestDataSchema, + type PackageListingApproveRequestData, + type PackageListingApproveRequestParams, + type PackageListingRejectRequestData, + type PackageListingRejectRequestParams, + type PackageListingReportRequestData, + type PackageListingReportRequestParams, + type PackageListingUpdateRequestData, + type PackageListingUpdateRequestParams, packageListingApproveRequestDataSchema, packageListingRejectRequestDataSchema, - PackageListingReportRequestParams, - PackageListingReportRequestData, packageListingReportRequestDataSchema, + packageListingUpdateRequestDataSchema, } from "../schemas/requestSchemas"; import { - PackageListingUpdateResponseData, + type PackageListingUpdateResponseData, packageListingUpdateResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/post/packageWiki.ts b/packages/thunderstore-api/src/post/packageWiki.ts index aff63e75e..6b76093c9 100644 --- a/packages/thunderstore-api/src/post/packageWiki.ts +++ b/packages/thunderstore-api/src/post/packageWiki.ts @@ -1,17 +1,17 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - PackageWikiPageCreateRequestData, - PackageWikiPageCreateRequestParams, + type PackageWikiPageCreateRequestData, + type PackageWikiPageCreateRequestParams, + type PackageWikiPageEditRequestData, + type PackageWikiPageEditRequestParams, packageWikiPageCreateRequestDataSchema, - PackageWikiPageEditRequestData, - PackageWikiPageEditRequestParams, packageWikiPageEditRequestDataSchema, } from "../schemas/requestSchemas"; import { - PackageWikiPageCreateResponseData, + type PackageWikiPageCreateResponseData, + type PackageWikiPageEditResponseData, packageWikiPageCreateResponseDataSchema, - PackageWikiPageEditResponseData, packageWikiPageEditResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/post/submission.ts b/packages/thunderstore-api/src/post/submission.ts index 0f6ce3e3a..4dfd407f4 100644 --- a/packages/thunderstore-api/src/post/submission.ts +++ b/packages/thunderstore-api/src/post/submission.ts @@ -1,16 +1,16 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - PackageSubmissionRequestData, + type PackageSubmissionRequestData, + type SubmissionValidateManifestRequestData, packageSubmissionRequestDataSchema, - SubmissionValidateManifestRequestData, submissionValidateManifestRequestDataSchema, } from "../schemas/requestSchemas"; import { - PackageSubmissionResponseData, + type PackageSubmissionResponseData, + type SubmissionValidateManifestResponseData, packageSubmissionResponseDataSchema, submissionValidateManifestResponseDataSchema, - SubmissionValidateManifestResponseData, } from "../schemas/responseSchemas"; export function postPackageSubmission( diff --git a/packages/thunderstore-api/src/post/team.ts b/packages/thunderstore-api/src/post/team.ts index 509045d74..5b31d4e37 100644 --- a/packages/thunderstore-api/src/post/team.ts +++ b/packages/thunderstore-api/src/post/team.ts @@ -1,11 +1,11 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - TeamCreateRequestData, + type TeamCreateRequestData, teamCreateRequestDataSchema, } from "../schemas/requestSchemas"; import { - TeamCreateResponseData, + type TeamCreateResponseData, teamCreateResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/post/teamAddServiceAccount.ts b/packages/thunderstore-api/src/post/teamAddServiceAccount.ts index fb8947920..182243aa4 100644 --- a/packages/thunderstore-api/src/post/teamAddServiceAccount.ts +++ b/packages/thunderstore-api/src/post/teamAddServiceAccount.ts @@ -1,14 +1,15 @@ // THIS API ENDPOINT IS NOT IMPLEMENTED YET IN THE THUNDERSTORE API - -import { +import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps, TeamServiceAccountAddRequestData, - teamServiceAccountAddRequestDataSchema, TeamServiceAccountAddRequestParams, TeamServiceAccountAddResponseData, +} from "../index"; +import { + teamServiceAccountAddRequestDataSchema, teamServiceAccountAddResponseSchema, } from "../index"; -import { apiFetch } from "../apiFetch"; export type teamAddServiceAccountMetaArgs = { teamIdentifier: string; diff --git a/packages/thunderstore-api/src/post/teamMember.ts b/packages/thunderstore-api/src/post/teamMember.ts index 617eb4c1c..b06bb4717 100644 --- a/packages/thunderstore-api/src/post/teamMember.ts +++ b/packages/thunderstore-api/src/post/teamMember.ts @@ -1,12 +1,12 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - TeamAddMemberRequestData, + type TeamAddMemberRequestData, teamAddMemberRequestDataSchema, } from "../schemas/requestSchemas"; -import { TeamAddMemberRequestParams } from "../schemas/requestSchemas"; +import type { TeamAddMemberRequestParams } from "../schemas/requestSchemas"; import { - TeamAddMemberResponseData, + type TeamAddMemberResponseData, teamAddMemberResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/post/usermedia.ts b/packages/thunderstore-api/src/post/usermedia.ts index 77b799457..65b4c7a35 100644 --- a/packages/thunderstore-api/src/post/usermedia.ts +++ b/packages/thunderstore-api/src/post/usermedia.ts @@ -1,19 +1,19 @@ -import { ApiEndpointProps } from "../index"; import { apiFetch } from "../apiFetch"; +import type { ApiEndpointProps } from "../index"; import { - UsermediaAbortUploadRequestParams, - UsermediaFinishUploadRequestData, + type UsermediaAbortUploadRequestParams, + type UsermediaFinishUploadRequestData, + type UsermediaFinishUploadRequestParams, + type UsermediaInitiateUploadRequestData, usermediaFinishUploadRequestDataSchema, - UsermediaFinishUploadRequestParams, - UsermediaInitiateUploadRequestData, usermediaInitiateUploadRequestDataSchema, } from "../schemas/requestSchemas"; import { - UsermediaAbortUploadResponseData, + type UsermediaAbortUploadResponseData, + type UsermediaFinishUploadResponseData, + type UsermediaInitiateUploadResponseData, usermediaAbortUploadResponseDataSchema, - UsermediaFinishUploadResponseData, usermediaFinishUploadResponseDataSchema, - UsermediaInitiateUploadResponseData, usermediaInitiateUploadResponseDataSchema, } from "../schemas/responseSchemas"; diff --git a/packages/thunderstore-api/src/schemas/requestSchemas.ts b/packages/thunderstore-api/src/schemas/requestSchemas.ts index 491b4fdce..249c8924d 100644 --- a/packages/thunderstore-api/src/schemas/requestSchemas.ts +++ b/packages/thunderstore-api/src/schemas/requestSchemas.ts @@ -1,17 +1,18 @@ import { z } from "zod"; + import { usermediaCompletedPartSchema } from "./objectSchemas"; import { CommunityListOrderingEnum, communityListOrderingQueryParam, - includedCategoriesQueryParam, deprecatedQueryParam, excludedCategoriesQueryParam, + includedCategoriesQueryParam, nsfwQueryParam, + packageListingsOrderingQueryParam, pageQueryParam, qQueryParam, searchQueryParam, sectionQueryParam, - packageListingsOrderingQueryParam, } from "./queryParamSchemas"; // UsermediaInitiateUploadRequest diff --git a/packages/thunderstore-api/src/schemas/responseSchemas.ts b/packages/thunderstore-api/src/schemas/responseSchemas.ts index 5d67c5939..430336045 100644 --- a/packages/thunderstore-api/src/schemas/responseSchemas.ts +++ b/packages/thunderstore-api/src/schemas/responseSchemas.ts @@ -1,26 +1,27 @@ import { z } from "zod"; + import { - communitySchema, communityFiltersSchema, - userMediaSchema, - usermediaUploadPartUrlSchema, - packageListingSchema, - userSchema, + communitySchema, + currentUserTeamPermissionsSchema, emptyUserSchema, + markdownRenderSchema, packageListingDetailsSchema, + packageListingSchema, + packagePermissionsSchema, + packageSourceSchema, + packageSubmissionStatusSchema, + packageTeamSchema, + packageVersionDependencySchema, packageVersionSchema, + packageWikiPageSchema, ratedPackagesSchema, teamDetailsSchema, teamMembersSchema, teamServiceAccountSchema, - packageSubmissionStatusSchema, - markdownRenderSchema, - packageWikiPageSchema, - packagePermissionsSchema, - packageSourceSchema, - packageVersionDependencySchema, - packageTeamSchema, - currentUserTeamPermissionsSchema, + userMediaSchema, + userSchema, + usermediaUploadPartUrlSchema, } from "../schemas/objectSchemas"; import { paginatedResults } from "../schemas/objectSchemas"; diff --git a/packages/thunderstore-api/src/vite-env.d.ts b/packages/thunderstore-api/src/vite-env.d.ts new file mode 100644 index 000000000..cd7b9928c --- /dev/null +++ b/packages/thunderstore-api/src/vite-env.d.ts @@ -0,0 +1,6 @@ +interface ImportMeta { + readonly env: { + readonly VITE_THUNDERSTORE_TEST_API_HOST?: string; + readonly [key: string]: string | undefined; + }; +} diff --git a/packages/thunderstore-api/tsconfig.json b/packages/thunderstore-api/tsconfig.json index 509a9bfcb..781ba1671 100644 --- a/packages/thunderstore-api/tsconfig.json +++ b/packages/thunderstore-api/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, @@ -19,11 +19,12 @@ "experimentalDecorators": true, "resolveJsonModule": true, "forceConsistentCasingInFileNames": true, + "verbatimModuleSyntax": false, "composite": true, "outDir": "./dist", "rootDir": "./src", "jsx": "react-jsx", - "types": ["@vitest/browser/providers/playwright"] + "types": ["vite/client", "@vitest/browser/providers/playwright"] }, "include": ["./src/**/*.tsx", "./src/**/*.ts"], "exclude": ["node_modules"] diff --git a/packages/ts-api-react-actions/package.json b/packages/ts-api-react-actions/package.json index 1c89625c7..0d87a296f 100644 --- a/packages/ts-api-react-actions/package.json +++ b/packages/ts-api-react-actions/package.json @@ -5,7 +5,7 @@ "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/packages/ts-api-react-actions", "main": "dist/thunderstore-ts-api-react-actions.cjs.js", "module": "dist/thunderstore-ts-api-react-actions.esm.js", - "types": "dist/thunderstore-ts-api-react-actions.cjs.d.ts", + "types": "dist/index.d.ts", "files": [ "dist" ], diff --git a/packages/ts-api-react-actions/src/ApiAction.tsx b/packages/ts-api-react-actions/src/ApiAction.tsx index 7dfa37853..9c98d040a 100644 --- a/packages/ts-api-react-actions/src/ApiAction.tsx +++ b/packages/ts-api-react-actions/src/ApiAction.tsx @@ -1,6 +1,11 @@ import { useCallback } from "react"; -import { ApiEndpointProps, ApiError } from "@thunderstore/thunderstore-api"; -import { ApiEndpoint } from "@thunderstore/ts-api-react"; + +import type { + ApiEndpointProps, + ApiError, +} from "@thunderstore/thunderstore-api"; +import type { ApiEndpoint } from "@thunderstore/ts-api-react"; + import { useApiAction } from "./useApiAction"; export interface ApiActionProps< @@ -39,7 +44,7 @@ export function ApiAction< } } catch (e) { if (onSubmitError) { - onSubmitError(e); + onSubmitError(e instanceof Error ? e : new Error(String(e))); } else { throw e; } diff --git a/packages/ts-api-react-actions/src/useApiAction.ts b/packages/ts-api-react-actions/src/useApiAction.ts index 82a93321e..4686917c9 100644 --- a/packages/ts-api-react-actions/src/useApiAction.ts +++ b/packages/ts-api-react-actions/src/useApiAction.ts @@ -1,5 +1,5 @@ -import { ApiEndpoint, useApiCall } from "@thunderstore/ts-api-react"; import { ApiEndpointProps } from "@thunderstore/thunderstore-api"; +import { ApiEndpoint, useApiCall } from "@thunderstore/ts-api-react"; export type UseApiActionArgs = { endpoint: ApiEndpoint; diff --git a/packages/ts-api-react-actions/tsconfig.json b/packages/ts-api-react-actions/tsconfig.json index 51e494b37..71137caca 100644 --- a/packages/ts-api-react-actions/tsconfig.json +++ b/packages/ts-api-react-actions/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/ts-api-react-forms/package.json b/packages/ts-api-react-forms/package.json index 7d3231626..3b2554920 100644 --- a/packages/ts-api-react-forms/package.json +++ b/packages/ts-api-react-forms/package.json @@ -5,7 +5,7 @@ "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/packages/ts-api-react-forms", "main": "dist/thunderstore-ts-api-react-forms.cjs.js", "module": "dist/thunderstore-ts-api-react-forms.esm.js", - "types": "dist/thunderstore-ts-api-react-forms.cjs.d.ts", + "types": "dist/index.d.ts", "files": [ "dist" ], diff --git a/packages/ts-api-react-forms/src/ApiForm.tsx b/packages/ts-api-react-forms/src/ApiForm.tsx index 3d4a4cb61..a8ecfd925 100644 --- a/packages/ts-api-react-forms/src/ApiForm.tsx +++ b/packages/ts-api-react-forms/src/ApiForm.tsx @@ -1,14 +1,16 @@ "use client"; import { HTMLAttributes, PropsWithChildren, useCallback } from "react"; +import { FormProvider } from "react-hook-form"; +import z, { ZodObject, ZodRawShape } from "zod"; + import { ApiEndpointProps, ApiError, RequestConfig, } from "@thunderstore/thunderstore-api"; -import { FormProvider } from "react-hook-form"; + import { useApiForm } from "./useApiForm"; -import z, { ZodObject, ZodRawShape } from "zod"; export function ApiForm< Params extends object, diff --git a/packages/ts-api-react-forms/src/errors.ts b/packages/ts-api-react-forms/src/errors.ts index a1a222e86..fd90280d0 100644 --- a/packages/ts-api-react-forms/src/errors.ts +++ b/packages/ts-api-react-forms/src/errors.ts @@ -1,6 +1,7 @@ -import { ApiError, isApiError } from "@thunderstore/thunderstore-api"; -import { ZodObject, ZodRawShape } from "zod"; import { Path, UseFormSetError } from "react-hook-form"; +import { ZodObject, ZodRawShape } from "zod"; + +import { ApiError, isApiError } from "@thunderstore/thunderstore-api"; // TODO: The types and schema usage might be super stupid here export function getErrorFormKey< diff --git a/packages/ts-api-react-forms/src/useApiForm.ts b/packages/ts-api-react-forms/src/useApiForm.ts index 8efeb1f2f..5802807b2 100644 --- a/packages/ts-api-react-forms/src/useApiForm.ts +++ b/packages/ts-api-react-forms/src/useApiForm.ts @@ -1,10 +1,12 @@ -import { useForm, UseFormReturn } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { handleFormApiErrors } from "./errors"; -import { ApiEndpoint, useApiCall } from "@thunderstore/ts-api-react"; -import { ApiEndpointProps } from "@thunderstore/thunderstore-api"; +import { UseFormReturn, useForm } from "react-hook-form"; import { ZodObject, ZodRawShape } from "zod"; +import { ApiEndpointProps } from "@thunderstore/thunderstore-api"; +import { ApiEndpoint, useApiCall } from "@thunderstore/ts-api-react"; + +import { handleFormApiErrors } from "./errors"; + export type UseApiFormArgs< Params extends object, QueryParams extends object, diff --git a/packages/ts-api-react-forms/tsconfig.json b/packages/ts-api-react-forms/tsconfig.json index afe00238d..095f39406 100644 --- a/packages/ts-api-react-forms/tsconfig.json +++ b/packages/ts-api-react-forms/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/ts-api-react/package.json b/packages/ts-api-react/package.json index 81e569ce2..43cb35c96 100644 --- a/packages/ts-api-react/package.json +++ b/packages/ts-api-react/package.json @@ -5,7 +5,7 @@ "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/packages/ts-api-react", "main": "dist/thunderstore-ts-api-react.cjs.js", "module": "dist/thunderstore-ts-api-react.esm.js", - "types": "dist/thunderstore-ts-api-react.cjs.d.ts", + "types": "dist/index.d.ts", "files": [ "dist" ], diff --git a/packages/ts-api-react/src/SessionContext.tsx b/packages/ts-api-react/src/SessionContext.tsx index b943cc8be..3c8f38686 100644 --- a/packages/ts-api-react/src/SessionContext.tsx +++ b/packages/ts-api-react/src/SessionContext.tsx @@ -1,15 +1,17 @@ "use client"; -import { StorageManager } from "./storage"; -import { - User, - userSchema, - EmptyUser, - RequestConfig, -} from "@thunderstore/thunderstore-api"; // Probably shouldn't from Dapper, but what can you do when you need these. // import { CurrentUser } from "@thunderstore/dapper/types"; import { DapperTs } from "@thunderstore/dapper-ts"; +import { + type EmptyUser, + type RequestConfig, + type User, + userSchema, +} from "@thunderstore/thunderstore-api"; + +import { StorageManager } from "./storage"; + // import { CurrentUser } from "@thunderstore/dapper/types"; export interface ContextInterface { @@ -17,6 +19,10 @@ export interface ContextInterface { clearSession: (clearApiHost?: boolean) => void; /** Remove session cookies. */ clearCookies: (domain: string) => void; + /** + * Clear persisted session data (current user, cookies) and mark session as stale. + */ + clearInvalidSession: (cookieDomainOverride?: string) => void; /** Set SessionData in storage */ setSession: (sessionData: SessionData) => void; /** Set session stale state */ @@ -99,6 +105,29 @@ export const clearCookies = (domain: string) => { deleteCookie("sessionid", domain); }; +export const clearInvalidSession = ( + _storage: StorageManager, + cookieDomainOverride?: string +) => { + if (typeof window === "undefined") { + return; + } + try { + // Preserve API host to allow session recovery / re-authentication flows. + clearSession(_storage, false); + const cookieDomain = + cookieDomainOverride || + _storage.safeGetValue(COOKIE_DOMAIN_KEY) || + undefined; + if (cookieDomain) { + clearCookies(cookieDomain); + } + setSessionStale(_storage, true); + } catch (error) { + console.error("Failed to clear invalid session", error); + } +}; + export const getConfig = ( _storage: StorageManager, domain?: string @@ -178,10 +207,7 @@ export const updateCurrentUser = async ( customClearSession ? customClearSession : () => { - // This function gets called when the dapper getCurrentUser gets 401 as a response - clearSession(_storage, false); - // We want to clear the sessionid cookie if it's invalid. - clearCookies(_storage.safeGetValue(COOKIE_DOMAIN_KEY) || ""); + clearInvalidSession(_storage); } ); const currentUser = await dapper.getCurrentUser(); @@ -247,6 +273,13 @@ export const getSessionContext = ( clearCookies(domain); }; + const _clearInvalidSession = (cookieDomainOverride?: string) => { + clearInvalidSession( + _storage, + cookieDomainOverride || cookieDomain || undefined + ); + }; + const _getConfig = (domain?: string): RequestConfig => { return getConfig(_storage, domain); }; @@ -289,6 +322,7 @@ export const getSessionContext = ( return { clearSession: _clearSession, clearCookies: _clearCookies, + clearInvalidSession: _clearInvalidSession, getConfig: _getConfig, runSessionValidationCheck: _runSessionValidationCheck, updateCurrentUser: _updateCurrentUser, diff --git a/packages/ts-api-react/src/index.ts b/packages/ts-api-react/src/index.ts index 4c85be554..c25c996e1 100644 --- a/packages/ts-api-react/src/index.ts +++ b/packages/ts-api-react/src/index.ts @@ -1,15 +1,25 @@ export { API_HOST_KEY, + COOKIE_DOMAIN_KEY, CURRENT_USER_KEY, + SESSION_STORAGE_KEY, + STALE_KEY, + getCookie, setSession, + setSessionStale, clearSession, + clearCookies, + clearInvalidSession, getConfig, + getSessionContext, + getSessionStale, runSessionValidationCheck, storeCurrentUser, updateCurrentUser, getSessionCurrentUser, } from "./SessionContext"; export type { ContextInterface } from "./SessionContext"; +export { StorageManager } from "./storage"; export { StorageManager as NamespacedStorageManager } from "./storage"; export { useApiCall } from "./useApiCall"; export type { ApiEndpoint } from "./useApiCall"; diff --git a/packages/ts-api-react/tsconfig.json b/packages/ts-api-react/tsconfig.json index afe00238d..095f39406 100644 --- a/packages/ts-api-react/tsconfig.json +++ b/packages/ts-api-react/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/ts-uploader-react/package.json b/packages/ts-uploader-react/package.json index 39a979664..5ea5b97b6 100644 --- a/packages/ts-uploader-react/package.json +++ b/packages/ts-uploader-react/package.json @@ -14,6 +14,7 @@ "dev": "tsc --watch" }, "dependencies": { + "@babel/runtime": "^7.25.6", "@thunderstore/ts-uploader": "*", "@types/react": "^19.1.0", "react": "^19.1.0", diff --git a/packages/ts-uploader-react/src/index.tsx b/packages/ts-uploader-react/src/index.tsx index fe9829461..41df87639 100644 --- a/packages/ts-uploader-react/src/index.tsx +++ b/packages/ts-uploader-react/src/index.tsx @@ -1,10 +1,11 @@ +import { useEffect, useState } from "react"; + import { IBaseUploadHandle, + UploadError, UploadProgress, UploadStatus, - UploadError, } from "@thunderstore/ts-uploader"; -import { useEffect, useState } from "react"; export const useUploadProgress = ( handle?: IBaseUploadHandle diff --git a/packages/ts-uploader-react/tsconfig.json b/packages/ts-uploader-react/tsconfig.json index afe00238d..095f39406 100644 --- a/packages/ts-uploader-react/tsconfig.json +++ b/packages/ts-uploader-react/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/ts-uploader/package.json b/packages/ts-uploader/package.json index 6ca337433..7ad7f6c88 100644 --- a/packages/ts-uploader/package.json +++ b/packages/ts-uploader/package.json @@ -3,19 +3,20 @@ "version": "0.1.0", "description": "Handles file uploads to Thunderstore", "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/packages/ts-uploader", - "main": "dist/thunderstore-ts-uploader.cjs.js", - "module": "dist/thunderstore-ts-uploader.esm.js", - "types": "dist/thunderstore-ts-uploader.cjs.d.ts", + "main": "dist/thunderstore-ts-uploader.umd.cjs", + "module": "dist/thunderstore-ts-uploader.js", + "types": "dist/index.d.ts", "files": [ "dist" ], "scripts": { - "build": "tsc", - "dev": "tsc --watch", + "build": "vite build", + "dev": "vite build --watch", "test": "vitest", "test:coverage": "vitest run --coverage" }, "dependencies": { + "@babel/runtime": "^7.25.6", "@thunderstore/graph-system": "^0.1.0", "@thunderstore/thunderstore-api": "^0.1.0", "@thunderstore/typed-event-emitter": "*", @@ -26,6 +27,16 @@ "@types/crypto-js": "^4.1.3", "@vitest/browser": "3.2.4", "playwright": "1.55.1", + "vite": "^7.2.6", + "vite-plugin-dts": "^4.5.4", "vitest": "3.2.4" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/thunderstore-ts-uploader.js", + "require": "./dist/thunderstore-ts-uploader.umd.cjs" + }, + "./package.json": "./package.json" } } diff --git a/packages/ts-uploader/src/index.ts b/packages/ts-uploader/src/index.ts index 98c112e2c..0e5b386da 100644 --- a/packages/ts-uploader/src/index.ts +++ b/packages/ts-uploader/src/index.ts @@ -7,4 +7,5 @@ export type { UploadPartStatus, UploadError, UploadMetrics, + UserMedia, } from "./uploaders/types"; diff --git a/packages/ts-uploader/src/uploaders/BaseUpload.ts b/packages/ts-uploader/src/uploaders/BaseUpload.ts index 280cfbf09..d40d5781e 100644 --- a/packages/ts-uploader/src/uploaders/BaseUpload.ts +++ b/packages/ts-uploader/src/uploaders/BaseUpload.ts @@ -1,4 +1,5 @@ import { TypedEventEmitter } from "@thunderstore/typed-event-emitter"; + import { IBaseUploadHandle, UploadConfig, diff --git a/packages/ts-uploader/src/uploaders/MultipartUpload.ts b/packages/ts-uploader/src/uploaders/MultipartUpload.ts index 9fb50d8d3..59341db67 100644 --- a/packages/ts-uploader/src/uploaders/MultipartUpload.ts +++ b/packages/ts-uploader/src/uploaders/MultipartUpload.ts @@ -1,3 +1,14 @@ +import { GraphExecutor, GraphNode } from "@thunderstore/graph-system"; +import { + RequestConfig, + postUsermediaAbort, + postUsermediaFinish, + postUsermediaInitiate, +} from "@thunderstore/thunderstore-api"; +import { TypedEventEmitter } from "@thunderstore/typed-event-emitter"; + +import { MD5WorkerManager, getMD5WorkerManager } from "../workers"; +import { BaseUpload } from "./BaseUpload"; import { CompleteUpload, FinalizedUpload, @@ -7,22 +18,12 @@ import { UploadStatus, UserMedia, } from "./types"; -import { TypedEventEmitter } from "@thunderstore/typed-event-emitter"; -import { GraphNode, GraphExecutor } from "@thunderstore/graph-system"; -import { BaseUpload } from "./BaseUpload"; import { - UploadConfig, - UploadProgress, MultiPartUploadOptions, + UploadConfig, UploadPartStatus, + UploadProgress, } from "./types"; -import { getMD5WorkerManager, MD5WorkerManager } from "../workers"; -import { - postUsermediaAbort, - postUsermediaFinish, - postUsermediaInitiate, - RequestConfig, -} from "@thunderstore/thunderstore-api"; import { slicePart } from "./utls"; export interface UploadPart { diff --git a/packages/ts-uploader/src/uploaders/types.ts b/packages/ts-uploader/src/uploaders/types.ts index ddc9555e5..4f5c73e97 100644 --- a/packages/ts-uploader/src/uploaders/types.ts +++ b/packages/ts-uploader/src/uploaders/types.ts @@ -1,9 +1,10 @@ import { RequestConfig } from "@thunderstore/thunderstore-api"; import { TypedEventEmitter } from "@thunderstore/typed-event-emitter"; + import { + CompletePartState, PartState, PreparedPartState, - CompletePartState, } from "./MultipartUpload"; export type UploadType = "single" | "multipart"; diff --git a/packages/ts-uploader/src/workers/__tests__/MD5WorkerManager.test.ts b/packages/ts-uploader/src/workers/__tests__/MD5WorkerManager.test.ts index 01711800f..ee8203061 100644 --- a/packages/ts-uploader/src/workers/__tests__/MD5WorkerManager.test.ts +++ b/packages/ts-uploader/src/workers/__tests__/MD5WorkerManager.test.ts @@ -1,4 +1,5 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + import { MD5WorkerManager } from "../MD5WorkerManager"; describe("MD5WorkerManager", () => { diff --git a/packages/ts-uploader/tsconfig.json b/packages/ts-uploader/tsconfig.json index 509a9bfcb..b11221f04 100644 --- a/packages/ts-uploader/tsconfig.json +++ b/packages/ts-uploader/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, @@ -20,6 +20,9 @@ "resolveJsonModule": true, "forceConsistentCasingInFileNames": true, "composite": true, + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, "outDir": "./dist", "rootDir": "./src", "jsx": "react-jsx", diff --git a/packages/ts-uploader/vite.config.ts b/packages/ts-uploader/vite.config.ts new file mode 100644 index 000000000..5da9e1d03 --- /dev/null +++ b/packages/ts-uploader/vite.config.ts @@ -0,0 +1,24 @@ +import { resolve } from "path"; +import { defineConfig } from "vite"; +import dts from "vite-plugin-dts"; + +export default defineConfig({ + base: "./", + build: { + lib: { + entry: resolve(__dirname, "src/index.ts"), + name: "ts-uploader", + fileName: (format) => + `thunderstore-ts-uploader.${format === "es" ? "js" : "umd.cjs"}`, + formats: ["es", "umd"], + }, + rollupOptions: { + external: [ + "@thunderstore/graph-system", + "@thunderstore/thunderstore-api", + "@thunderstore/typed-event-emitter", + ], + }, + }, + plugins: [dts({ rollupTypes: true })], +}); diff --git a/packages/typed-event-emitter/package.json b/packages/typed-event-emitter/package.json index 231297a80..0c7f4fb43 100644 --- a/packages/typed-event-emitter/package.json +++ b/packages/typed-event-emitter/package.json @@ -15,7 +15,9 @@ "test": "vitest run", "watch": "vitest watch" }, - "dependencies": {}, + "dependencies": { + "@babel/runtime": "^7.25.6" + }, "devDependencies": { "@vitest/browser": "3.2.4", "playwright": "1.55.1", diff --git a/packages/typed-event-emitter/src/__tests__/TypedEventEmitter.test.ts b/packages/typed-event-emitter/src/__tests__/TypedEventEmitter.test.ts index ce817d309..1a9e7d58c 100644 --- a/packages/typed-event-emitter/src/__tests__/TypedEventEmitter.test.ts +++ b/packages/typed-event-emitter/src/__tests__/TypedEventEmitter.test.ts @@ -1,4 +1,5 @@ -import { describe, it, expect, beforeEach } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; + import { TypedEventEmitter, TypedListener } from "../TypedEventEmitter"; describe("TypedEventEmitter", () => { diff --git a/packages/typed-event-emitter/tsconfig.json b/packages/typed-event-emitter/tsconfig.json index b19be4aea..af458a7b8 100644 --- a/packages/typed-event-emitter/tsconfig.json +++ b/packages/typed-event-emitter/tsconfig.json @@ -3,7 +3,7 @@ "target": "es5", "module": "ES2020", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/packages/use-promise/package.json b/packages/use-promise/package.json index d0cdf3587..8441b3ffc 100644 --- a/packages/use-promise/package.json +++ b/packages/use-promise/package.json @@ -5,7 +5,7 @@ "repository": "https://github.com/thunderstore-io/thunderstore-ui/tree/master/packages/use-promise", "main": "dist/thunderstore-use-promise.cjs.js", "module": "dist/thunderstore-use-promise.esm.js", - "types": "dist/thunderstore-use-promise.cjs.d.ts", + "types": "dist/index.d.ts", "files": [ "dist" ], diff --git a/packages/use-promise/src/index.ts b/packages/use-promise/src/index.ts index e68e4a1dc..7b203ec67 100644 --- a/packages/use-promise/src/index.ts +++ b/packages/use-promise/src/index.ts @@ -3,9 +3,9 @@ * Based on react-promise-suspense v0.3.4. Improves caching by comparing * the names of the functions passed to usePromise. */ - // Changed for Thunderstore: use lodash instead of fast-deep-equal import lodash from "lodash"; + const { isEqual } = lodash; interface PromiseCache { diff --git a/packages/use-promise/tsconfig.json b/packages/use-promise/tsconfig.json index 51e494b37..71137caca 100644 --- a/packages/use-promise/tsconfig.json +++ b/packages/use-promise/tsconfig.json @@ -3,7 +3,7 @@ "target": "ESNext", "module": "ESNext", "skipLibCheck": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "removeComments": true, "noImplicitAny": true, "strictNullChecks": true, diff --git a/tools/cyberstorm-playwright/tests/communities.spec.ts b/tools/cyberstorm-playwright/tests/communities.spec.ts index e163e3a14..6c6b9089b 100644 --- a/tools/cyberstorm-playwright/tests/communities.spec.ts +++ b/tools/cyberstorm-playwright/tests/communities.spec.ts @@ -1,5 +1,5 @@ // import { test, expect } from "@chromatic-com/playwright"; -import { test, expect } from "@playwright/test"; +import { expect, test } from "@playwright/test"; const percySnapshot = require("@percy/playwright"); diff --git a/tools/cyberstorm-playwright/tests/community.spec.ts b/tools/cyberstorm-playwright/tests/community.spec.ts index a7091eec9..589d7fcc6 100644 --- a/tools/cyberstorm-playwright/tests/community.spec.ts +++ b/tools/cyberstorm-playwright/tests/community.spec.ts @@ -1,7 +1,6 @@ // import { test, expect } from "@chromatic-com/playwright"; -import { test } from "@playwright/test"; - import percySnapshot from "@percy/playwright"; +import { test } from "@playwright/test"; test("community page", async ({ page }) => { await page.goto("http://localhost:3000/c/riskofrain2"); diff --git a/tools/nginx/new-localhost.conf b/tools/nginx/new-thunderstore-localhost.conf similarity index 77% rename from tools/nginx/new-localhost.conf rename to tools/nginx/new-thunderstore-localhost.conf index e5ee93ea9..60c1e6463 100644 --- a/tools/nginx/new-localhost.conf +++ b/tools/nginx/new-thunderstore-localhost.conf @@ -5,13 +5,13 @@ map $http_upgrade $connection_upgrade { server { listen 80; - server_name new.localhost; + server_name new.thunderstore.localhost; # Resolve container DNS dynamically to avoid stale IPs. resolver 127.0.0.11 ipv6=off valid=30s; location / { - # Ensure requests to new.localhost are never served from the backend nginx proxy cache. + # Ensure requests to new.thunderstore.localhost are never served from the backend nginx proxy cache. proxy_cache off; proxy_cache_bypass 1; proxy_no_cache 1; diff --git a/tools/scripts/run_test_container.mjs b/tools/scripts/run_test_container.mjs new file mode 100644 index 000000000..4ad8257a9 --- /dev/null +++ b/tools/scripts/run_test_container.mjs @@ -0,0 +1,183 @@ +import { spawn } from "node:child_process"; +import process from "node:process"; + +function usage(exitCode = 1) { + // Keep this short: it shows up in CI logs. + console.error( + "Usage: node tools/scripts/run_test_container.mjs [-- ]" + ); + process.exit(exitCode); +} + +function spawnLogged(command, args, options = {}) { + const printable = [command, ...args].join(" "); + console.log(printable); + + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + stdio: "inherit", + shell: false, + ...options, + }); + + child.on("error", reject); + child.on("close", (code) => { + if (code === 0) { + resolve(); + } else { + const err = new Error(`${printable} failed with exit code ${code}`); + err.exitCode = code; + reject(err); + } + }); + }); +} + +async function waitForDjangoInContainer( + composeFile, + { timeoutMs = 120_000, pollMs = 1_000 } = {} +) { + const deadline = Date.now() + timeoutMs; + + const pythonCheck = + "import http.client, sys; " + + "c=http.client.HTTPConnection('127.0.0.1',8000,timeout=2); " + + "c.request('GET','/'); " + + "r=c.getresponse(); " + + "sys.exit(0 if 200 <= r.status < 400 else 1)"; + + while (Date.now() < deadline) { + try { + await spawnLogged( + "docker", + [ + "compose", + "-f", + composeFile, + "exec", + "-T", + "django", + "python", + "-c", + pythonCheck, + ], + { + env: process.env, + stdio: "ignore", + } + ); + return; + } catch { + // ignore until timeout + } + + const remainingSeconds = Math.max( + 0, + Math.round((deadline - Date.now()) / 1000) + ); + console.log( + `Waiting for django to be ready inside container (${remainingSeconds}s remaining)` + ); + await new Promise((r) => setTimeout(r, pollMs)); + } + + throw new Error("Timed out waiting for django to be ready inside container"); +} + +function parseArgs(argv) { + const [subcommand, ...rest] = argv; + if (!subcommand || subcommand === "-h" || subcommand === "--help") { + usage(0); + } + + if (subcommand !== "test" && subcommand !== "coverage") { + console.error(`Unknown subcommand: ${subcommand}`); + usage(1); + } + + const dashDashIndex = rest.indexOf("--"); + // Yarn v1 forwards args without requiring `--`. Accept both forms: + // yarn test:container path/to/test + // yarn test:container -- path/to/test + const vitestArgs = + dashDashIndex === -1 ? rest : rest.slice(dashDashIndex + 1); + + return { subcommand, vitestArgs }; +} + +async function main() { + const { subcommand, vitestArgs } = parseArgs(process.argv.slice(2)); + + const backendComposeFile = + "tools/thunderstore-test-backend/docker-compose.yml"; + + // The test runner executes *inside* Docker. That container can resolve + // service names like `django`, but it can't resolve the host-published + // address `127.0.0.1:8000`. Override the backend's canonical host so + // redirects stay usable from within the container network. + const backendComposeEnv = { + ...process.env, + PRIMARY_HOST: process.env.PRIMARY_HOST ?? "django:8000", + }; + + try { + // Start backend in the background. + await spawnLogged( + "docker", + [ + "compose", + "-f", + backendComposeFile, + "up", + "-d", + "--remove-orphans", + "--build", + // Only start backend dependencies; the same compose file also contains + // the test runner services. + "db", + "redis", + "rabbitmq", + "minio", + "django", + ], + { env: backendComposeEnv } + ); + + // Wait until it's actually serving traffic (cold starts can take a while). + // We probe from inside the container network so we don't depend on any + // published host port and we implicitly wait for the backend setup + // commands inside the containers to finish. + await waitForDjangoInContainer(backendComposeFile, { timeoutMs: 300_000 }); + + const command = subcommand === "test" ? "test" : "coverage"; + + const args = [ + "compose", + "-f", + backendComposeFile, + "run", + "--rm", + "--build", + "cyberstorm-tests", + "yarn", + command, + ...vitestArgs, + ]; + + await spawnLogged("docker", args, { env: backendComposeEnv }); + } finally { + // Always tear down the test backend so it doesn't conflict with dev. + await spawnLogged( + "docker", + ["compose", "-f", backendComposeFile, "down", "--remove-orphans"], + { env: backendComposeEnv } + ).catch(() => { + // Best-effort cleanup. + }); + } +} + +main().catch((err) => { + console.error(err?.stack || String(err)); + process.exit(typeof err?.exitCode === "number" ? err.exitCode : 1); +}); diff --git a/tools/test-ci/run_ci_script.py b/tools/test-ci/run_ci_script.py index 2f2909feb..042b44b2d 100644 --- a/tools/test-ci/run_ci_script.py +++ b/tools/test-ci/run_ci_script.py @@ -97,7 +97,7 @@ def poll_backend() -> bool: except Exception: return False - timeout_threshold = time.time() + 60 + timeout_threshold = time.time() + 90 while (result := poll_backend()) is False and time.time() < timeout_threshold: print( "Polling failed, " @@ -112,12 +112,26 @@ def poll_backend() -> bool: def start_backend() -> bool: - run_command("docker compose up -d", cwd=BACKEND_DIR) + run_command( + [ + "docker", + "compose", + "up", + "-d", + "--remove-orphans", + "db", + "redis", + "rabbitmq", + "minio", + "django", + ], + cwd=BACKEND_DIR, + ) return wait_for_url("http://127.0.0.1:8000/") def stop_backend(): - run_command("docker compose down", cwd=BACKEND_DIR) + run_command(["docker", "compose", "down", "--remove-orphans"], cwd=BACKEND_DIR) def run_tests(): diff --git a/tools/thunderstore-test-backend/Dockerfile.test b/tools/thunderstore-test-backend/Dockerfile.test new file mode 100644 index 000000000..5c2541ace --- /dev/null +++ b/tools/thunderstore-test-backend/Dockerfile.test @@ -0,0 +1,29 @@ +FROM node:24-bookworm + +WORKDIR /workspace + +ARG PLAYWRIGHT_VERSION=1.55.1 + +ENV NODE_ENV=test + +RUN apt-get update \ + && apt-get install -y --no-install-recommends rsync \ + && rm -rf /var/lib/apt/lists/* + +# Install Playwright + its OS deps and browsers at image build time so +# containers don't redo this on every start. +ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright +RUN npm -g i "playwright@${PLAYWRIGHT_VERSION}" \ + && npx playwright install-deps chromium \ + && npx playwright install chromium \ + && mkdir -p "$PLAYWRIGHT_BROWSERS_PATH" \ + && chown -R node:node "$PLAYWRIGHT_BROWSERS_PATH" + +# Copy entrypoint script +COPY entrypoint.test.sh /usr/local/bin/entrypoint.test.sh +RUN chmod +x /usr/local/bin/entrypoint.test.sh + +ENTRYPOINT ["/usr/local/bin/entrypoint.test.sh"] + +# Default: run the repo test script (vitest) +CMD ["yarn", "test"] diff --git a/tools/thunderstore-test-backend/docker-compose.yml b/tools/thunderstore-test-backend/docker-compose.yml index 8c2429645..8c18c55ab 100644 --- a/tools/thunderstore-test-backend/docker-compose.yml +++ b/tools/thunderstore-test-backend/docker-compose.yml @@ -1,9 +1,10 @@ version: "3.8" x-django-service: &django-service - image: ${DJANGO_IMAGE:-thunderstore/thunderstore:release-0.145.1} + image: ${DJANGO_IMAGE:-thunderstore/thunderstore:release-0.151.0} environment: CORS_ALLOWED_ORIGINS: "*" + CORS_ALLOW_ALL_ORIGINS: "True" CELERY_BROKER_URL: "pyamqp://django:django@rabbitmq/django" DATABASE_URL: "psql://django:django@db/django" REDIS_URL: "redis://redis:6379/0" @@ -37,7 +38,7 @@ x-django-service: &django-service USE_ASYNC_PACKAGE_SUBMISSION_FLOW: "True" USE_TIME_SERIES_PACKAGE_DOWNLOAD_METRICS: "True" ALLOWED_HOSTS: "*" - PRIMARY_HOST: "127.0.0.1:8000" + PRIMARY_HOST: "${PRIMARY_HOST:-127.0.0.1:8000}" depends_on: - db - redis @@ -80,3 +81,37 @@ services: - ./fix_migration.py:/app/fix_migration.py ports: - "127.0.0.1:8000:8000" + + cyberstorm-tests: + user: "0:0" + build: + context: . + dockerfile: Dockerfile.test + working_dir: /workspace + volumes: + - ../../:/src:ro + - workspace_test_src:/workspace + - thunderstore_ui_test_node_modules:/workspace/node_modules + - nimbus_test_node_modules:/workspace/apps/cyberstorm-remix/node_modules + - yarn_cache:/usr/local/share/.cache/yarn + environment: + NODE_ENV: test + NPM_CONFIG_USERCONFIG: /run/secrets/npmrc + PLAYWRIGHT_BROWSERS_PATH: /ms-playwright + VITE_THUNDERSTORE_TEST_API_HOST: "http://django:8000" + RSYNC_ARGS: "-a --delete --info=progress2 --exclude=.git --exclude=build-secrets --exclude=.npmrc --exclude=node_modules --exclude=.turbo --exclude=.cache --exclude=apps/cyberstorm-remix/build --exclude=apps/cyberstorm-remix/.react-router" + secrets: + - npmrc + extra_hosts: + - "host.docker.internal:host-gateway" + + +volumes: + workspace_test_src: + thunderstore_ui_test_node_modules: + nimbus_test_node_modules: + yarn_cache: + +secrets: + npmrc: + file: "../../build-secrets/.npmrc" diff --git a/tools/thunderstore-test-backend/entrypoint.test.sh b/tools/thunderstore-test-backend/entrypoint.test.sh new file mode 100644 index 000000000..4a276ab64 --- /dev/null +++ b/tools/thunderstore-test-backend/entrypoint.test.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +set -euo pipefail + +HOME_DIR="${HOME:-/root}" + +# Setup npmrc (auth) if provided +if [ -f /run/secrets/npmrc ]; then + # Keep tight perms; contains auth tokens. + mkdir -p "$HOME_DIR" + install -m 0600 /run/secrets/npmrc "$HOME_DIR/.npmrc" +fi + +export PLAYWRIGHT_BROWSERS_PATH="${PLAYWRIGHT_BROWSERS_PATH:-/ms-playwright}" + +did_sync="false" + +# Sync the repo into the persistent /workspace volume (if /src is mounted). +if [ -d /src ]; then + echo "Syncing source into /workspace..." + if ! command -v rsync >/dev/null 2>&1; then + echo "ERROR: rsync is required but was not found in PATH" >&2 + exit 127 + fi + + rsync_args_default=( + -a + --delete + --exclude=.git + --exclude=build-secrets + --exclude=.npmrc + --exclude=node_modules + --exclude=.turbo + --exclude=.cache + --exclude=apps/cyberstorm-remix/build + --exclude=apps/cyberstorm-remix/.react-router + ) + + # If you need args that contain spaces, provide one arg per line via RSYNC_ARGS_NL. + # Otherwise RSYNC_ARGS is supported for backwards compatibility (whitespace-splitting). + if [ -n "${RSYNC_ARGS_NL:-}" ]; then + rsync_args=() + while IFS= read -r line; do + [ -z "$line" ] && continue + rsync_args+=("$line") + done <<< "$RSYNC_ARGS_NL" + elif [ -n "${RSYNC_ARGS:-}" ]; then + echo "WARNING: RSYNC_ARGS is split on whitespace; use RSYNC_ARGS_NL for args containing spaces" >&2 + set -f + IFS=$' \t\n' read -r -a rsync_args <<< "$RSYNC_ARGS" + set +f + else + rsync_args=("${rsync_args_default[@]}") + fi + rsync "${rsync_args[@]}" /src/ /workspace/ + + did_sync="true" +fi + +mkdir -p \ + /workspace/node_modules \ + /workspace/apps/cyberstorm-remix/node_modules \ + /workspace/apps/cyberstorm-remix/.react-router \ + /usr/local/share/.cache/yarn + +# Install JS deps if missing +if [ -z "$(ls -A /workspace/node_modules 2>/dev/null || true)" ] || [ ! -x /workspace/node_modules/.bin/vitest ]; then + echo "Installing dependencies..." + if ! command -v yarn >/dev/null 2>&1; then + echo "ERROR: yarn is required but was not found in PATH" >&2 + exit 127 + fi + yarn install --frozen-lockfile --production=false +fi + +# `preconstruct dev` generates workspace-local link files that rsync will delete on the next sync. +# When node_modules is cached, we still need to recreate those links so Vite can resolve package +# entrypoints (e.g. @thunderstore/dapper-ts, @thunderstore/thunderstore-api). +if [ "$did_sync" = "true" ]; then + if ! command -v yarn >/dev/null 2>&1; then + echo "ERROR: yarn is required but was not found in PATH" >&2 + exit 127 + fi + echo "Refreshing workspace links (postinstall)..." + yarn run -s postinstall +fi + +if [ "$#" -eq 0 ]; then + set -- yarn test +fi + +exec "$@" diff --git a/tools/visual-diff-ci/run_ci_script.py b/tools/visual-diff-ci/run_ci_script.py index d2ea6e70c..d8b5a8f73 100644 --- a/tools/visual-diff-ci/run_ci_script.py +++ b/tools/visual-diff-ci/run_ci_script.py @@ -158,7 +158,7 @@ def poll_backend() -> bool: except Exception: return False - timeout_threshold = time.time() + 60 + timeout_threshold = time.time() + 90 while (result := poll_backend()) is False and time.time() < timeout_threshold: print( "Polling failed, " @@ -173,12 +173,26 @@ def poll_backend() -> bool: def start_backend() -> bool: - run_command("docker compose up -d", cwd=BACKEND_DIR) + run_command( + [ + "docker", + "compose", + "up", + "-d", + "--remove-orphans", + "db", + "redis", + "rabbitmq", + "minio", + "django", + ], + cwd=BACKEND_DIR, + ) return wait_for_url("http://127.0.0.1:8000/") def stop_backend(): - run_command("docker compose down", cwd=BACKEND_DIR) + run_command(["docker", "compose", "down", "--remove-orphans"], cwd=BACKEND_DIR) PLAYWRIGHT_DIR = (REPO_ROOT / "tools" / "cyberstorm-playwright").resolve() diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..a63a8ee2e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "files": [], + "references": [ + { "path": "packages/thunderstore-api" }, + { "path": "packages/dapper" }, + { "path": "packages/dapper-fake" }, + { "path": "packages/dapper-ts" }, + { "path": "packages/ts-api-react" }, + { "path": "packages/ts-api-react-actions" }, + { "path": "packages/ts-api-react-forms" }, + { "path": "packages/typed-event-emitter" }, + { "path": "packages/use-promise" }, + { "path": "packages/react-dnd" }, + { "path": "packages/ts-uploader" }, + { "path": "packages/ts-uploader-react" }, + { "path": "packages/cyberstorm-theme" }, + { "path": "packages/cyberstorm" }, + { "path": "packages/cyberstorm-forms" }, + { "path": "packages/graph-system" }, + { "path": "packages/beta-switch" }, + { "path": "apps/cyberstorm-remix" }, + { "path": "apps/storybook" } + ] +} diff --git a/yarn.lock b/yarn.lock index 99a0e6405..ff257f709 100644 --- a/yarn.lock +++ b/yarn.lock @@ -108,6 +108,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" +"@babel/generator@^7.28.0": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" + integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== + dependencies: + "@babel/parser" "^7.28.5" + "@babel/types" "^7.28.5" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + "@babel/generator@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e" @@ -257,6 +268,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + "@babel/helper-validator-option@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" @@ -301,6 +317,13 @@ dependencies: "@babel/types" "^7.27.3" +"@babel/parser@^7.28.0", "@babel/parser@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" + integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== + dependencies: + "@babel/types" "^7.28.5" + "@babel/parser@^7.28.3", "@babel/parser@^7.28.4": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.4.tgz#da25d4643532890932cc03f7705fe19637e03fa8" @@ -1033,6 +1056,14 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@babel/types@^7.28.0", "@babel/types@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" + integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/types@^7.28.2", "@babel/types@^7.28.4": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.4.tgz#0a4e618f4c60a7cd6c11cb2d48060e4dbe38ac3a" @@ -1356,6 +1387,18 @@ resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.12.tgz#667d6254cc7ba3b0c010a323d78024a1d30c6053" integrity sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ== +"@isaacs/balanced-match@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" + integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== + +"@isaacs/brace-expansion@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3" + integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA== + dependencies: + "@isaacs/balanced-match" "^4.0.1" + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -1508,6 +1551,35 @@ dependencies: "@types/mdx" "^2.0.0" +"@microsoft/api-extractor-model@7.32.1": + version "7.32.1" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.32.1.tgz#9cc21bba2cd199048d205bbe9a946cb9f9f7bd2b" + integrity sha512-u4yJytMYiUAnhcNQcZDTh/tVtlrzKlyKrQnLOV+4Qr/5gV+cpufWzCYAB1Q23URFqD6z2RoL2UYncM9xJVGNKA== + dependencies: + "@microsoft/tsdoc" "~0.16.0" + "@microsoft/tsdoc-config" "~0.18.0" + "@rushstack/node-core-library" "5.19.0" + +"@microsoft/api-extractor@^7.50.1": + version "7.55.1" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.55.1.tgz#ae2211376deeb73ab5fe04cbe7cd13b7433d0651" + integrity sha512-l8Z+8qrLkZFM3HM95Dbpqs6G39fpCa7O5p8A7AkA6hSevxkgwsOlLrEuPv0ADOyj5dI1Af5WVDiwpKG/ya5G3w== + dependencies: + "@microsoft/api-extractor-model" "7.32.1" + "@microsoft/tsdoc" "~0.16.0" + "@microsoft/tsdoc-config" "~0.18.0" + "@rushstack/node-core-library" "5.19.0" + "@rushstack/rig-package" "0.6.0" + "@rushstack/terminal" "0.19.4" + "@rushstack/ts-command-line" "5.1.4" + diff "~8.0.2" + lodash "~4.17.15" + minimatch "10.0.3" + resolve "~1.22.1" + semver "~7.5.4" + source-map "~0.6.1" + typescript "5.8.2" + "@microsoft/eslint-formatter-sarif@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@microsoft/eslint-formatter-sarif/-/eslint-formatter-sarif-3.1.0.tgz#7fa03636e116939d77efaa301a31b340ca93059b" @@ -1518,6 +1590,21 @@ lodash "^4.17.14" utf8 "^3.0.0" +"@microsoft/tsdoc-config@~0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.18.0.tgz#02fd9924b888053ecbc3c0385a24532bf2106c98" + integrity sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw== + dependencies: + "@microsoft/tsdoc" "0.16.0" + ajv "~8.12.0" + jju "~1.4.0" + resolve "~1.22.2" + +"@microsoft/tsdoc@0.16.0", "@microsoft/tsdoc@~0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz#2249090633e04063176863a050c8f0808d2b6d2b" + integrity sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA== + "@mjackson/node-fetch-server@^0.2.0": version "0.2.0" resolved "https://registry.npmjs.org/@mjackson/node-fetch-server/-/node-fetch-server-0.2.0.tgz#577c0c25d8aae9f69a97738b7b0d03d1471cdc49" @@ -2575,7 +2662,7 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@rollup/pluginutils@^5.0.2": +"@rollup/pluginutils@^5.0.2", "@rollup/pluginutils@^5.1.4": version "5.3.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.3.0.tgz#57ba1b0cbda8e7a3c597a4853c807b156e21a7b4" integrity sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q== @@ -2794,6 +2881,52 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.2.tgz#cf83e2c56b581bad4614eeb3d2da5b5917ed34ec" integrity sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw== +"@rushstack/node-core-library@5.19.0": + version "5.19.0" + resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-5.19.0.tgz#6ac96b4c95769ec2b8b9e34c56e3502e52e712e7" + integrity sha512-BxAopbeWBvNJ6VGiUL+5lbJXywTdsnMeOS8j57Cn/xY10r6sV/gbsTlfYKjzVCUBZATX2eRzJHSMCchsMTGN6A== + dependencies: + ajv "~8.13.0" + ajv-draft-04 "~1.0.0" + ajv-formats "~3.0.1" + fs-extra "~11.3.0" + import-lazy "~4.0.0" + jju "~1.4.0" + resolve "~1.22.1" + semver "~7.5.4" + +"@rushstack/problem-matcher@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@rushstack/problem-matcher/-/problem-matcher-0.1.1.tgz#db9303ef3c47010c8aba5841e8c9511e091159df" + integrity sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA== + +"@rushstack/rig-package@0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.6.0.tgz#c80f93fe2c0d9d4977fc925ed9ce9decb75047a5" + integrity sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw== + dependencies: + resolve "~1.22.1" + strip-json-comments "~3.1.1" + +"@rushstack/terminal@0.19.4": + version "0.19.4" + resolved "https://registry.yarnpkg.com/@rushstack/terminal/-/terminal-0.19.4.tgz#2893129e29f4d640a7db9c78f5d7fbe44a27c726" + integrity sha512-f4XQk02CrKfrMgyOfhYd3qWI944dLC21S4I/LUhrlAP23GTMDNG6EK5effQtFkISwUKCgD9vMBrJZaPSUquxWQ== + dependencies: + "@rushstack/node-core-library" "5.19.0" + "@rushstack/problem-matcher" "0.1.1" + supports-color "~8.1.1" + +"@rushstack/ts-command-line@5.1.4": + version "5.1.4" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-5.1.4.tgz#970cf50bfb769a2479467932b68a4c9e663eda70" + integrity sha512-H0I6VdJ6sOUbktDFpP2VW5N29w8v4hRoNZOQz02vtEi6ZTYL1Ju8u+TcFiFawUDrUsx/5MQTUhd79uwZZVwVlA== + dependencies: + "@rushstack/terminal" "0.19.4" + "@types/argparse" "1.0.38" + argparse "~1.0.9" + string-argv "~0.3.1" + "@sentry-internal/browser-utils@10.9.0": version "10.9.0" resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-10.9.0.tgz#b98c4095356493c132a86d2e6680b7c704976309" @@ -3123,6 +3256,25 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.6.1.tgz#13e09a32d7a8b7060fe38304788ebf4197cd2149" integrity sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw== +"@trivago/prettier-plugin-sort-imports@6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-6.0.0.tgz#179dd4da55c6d055d76624bfe8b8be78ecddf014" + integrity sha512-Xarx55ow0R8oC7ViL5fPmDsg1EBa1dVhyZFVbFXNtPPJyW2w9bJADIla8YFSaNG9N06XfcklA9O9vmw4noNxkQ== + dependencies: + "@babel/generator" "^7.28.0" + "@babel/parser" "^7.28.0" + "@babel/traverse" "^7.28.0" + "@babel/types" "^7.28.0" + javascript-natural-sort "^0.7.1" + lodash-es "^4.17.21" + minimatch "^9.0.0" + parse-imports-exports "^0.2.4" + +"@types/argparse@1.0.38": + version "1.0.38" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" + integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== + "@types/aria-query@^5.0.1": version "5.0.4" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" @@ -3785,6 +3937,73 @@ loupe "^3.1.4" tinyrainbow "^2.0.0" +"@volar/language-core@2.4.26", "@volar/language-core@~2.4.11": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.4.26.tgz#ce2a2d6100e33906f48a4ece9211f17f42ad4c8d" + integrity sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A== + dependencies: + "@volar/source-map" "2.4.26" + +"@volar/source-map@2.4.26": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.4.26.tgz#b89b3663669f4eea448f8ff517b909beee354933" + integrity sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw== + +"@volar/typescript@^2.4.11": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.4.26.tgz#1928f2a206d7040949b9f021a9eb7c228752cd32" + integrity sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA== + dependencies: + "@volar/language-core" "2.4.26" + path-browserify "^1.0.1" + vscode-uri "^3.0.8" + +"@vue/compiler-core@3.5.25": + version "3.5.25" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz#7ffb658d7919348baad8c491eb5b948ee8e44108" + integrity sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw== + dependencies: + "@babel/parser" "^7.28.5" + "@vue/shared" "3.5.25" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.1" + +"@vue/compiler-dom@^3.5.0": + version "3.5.25" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz#dd799ac2474cda54303039310b8994f0cfb40957" + integrity sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q== + dependencies: + "@vue/compiler-core" "3.5.25" + "@vue/shared" "3.5.25" + +"@vue/compiler-vue2@^2.7.16": + version "2.7.16" + resolved "https://registry.yarnpkg.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz#2ba837cbd3f1b33c2bc865fbe1a3b53fb611e249" + integrity sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A== + dependencies: + de-indent "^1.0.2" + he "^1.2.0" + +"@vue/language-core@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-2.2.0.tgz#e48c54584f889f78b120ce10a050dfb316c7fcdf" + integrity sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw== + dependencies: + "@volar/language-core" "~2.4.11" + "@vue/compiler-dom" "^3.5.0" + "@vue/compiler-vue2" "^2.7.16" + "@vue/shared" "^3.5.0" + alien-signals "^0.4.9" + minimatch "^9.0.3" + muggle-string "^0.4.1" + path-browserify "^1.0.1" + +"@vue/shared@3.5.25", "@vue/shared@^3.5.0": + version "3.5.25" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.25.tgz#21edcff133a5a04f72c4e4c6142260963fe5afbe" + integrity sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg== + "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": version "1.14.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" @@ -3939,6 +4158,11 @@ acorn@^8.14.0, acorn@^8.8.1, acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -3954,6 +4178,11 @@ aggregate-error@^4.0.0: clean-stack "^4.0.0" indent-string "^5.0.0" +ajv-draft-04@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8" + integrity sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw== + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -3961,6 +4190,13 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" +ajv-formats@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" @@ -3988,6 +4224,31 @@ ajv@^8.0.0, ajv@^8.0.1, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" +ajv@~8.12.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ajv@~8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.13.0.tgz#a3939eaec9fb80d217ddf0c3376948c023f28c91" + integrity sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + +alien-signals@^0.4.9: + version "0.4.14" + resolved "https://registry.yarnpkg.com/alien-signals/-/alien-signals-0.4.14.tgz#9ff8f72a272300a51692f54bd9bbbada78fbf539" + integrity sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q== + ansi-colors@^4.1.1: version "4.1.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" @@ -4052,7 +4313,7 @@ arg@^5.0.1: resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== -argparse@^1.0.7: +argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -4929,6 +5190,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== +compare-versions@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.1.tgz#7af3cc1099ba37d244b3145a9af5201b629148a9" + integrity sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg== + component-emitter@^1.2.1: version "1.3.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" @@ -4959,6 +5225,16 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +confbox@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06" + integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== + +confbox@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.2.tgz#8652f53961c74d9e081784beed78555974a9c110" + integrity sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ== + config-chain@^1.1.11: version "1.1.13" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" @@ -5156,6 +5432,11 @@ dataloader@^2.0.0: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.3.tgz#42d10b4913515f5b37c6acedcb4960d6ae1b1517" integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" + integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -5170,6 +5451,13 @@ debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, d dependencies: ms "^2.1.3" +debug@^4.4.0: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + decamelize-keys@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -5340,6 +5628,11 @@ devlop@^1.0.0, devlop@^1.1.0: dependencies: dequal "^2.0.0" +diff@~8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-8.0.2.tgz#712156a6dd288e66ebb986864e190c2fc9eddfae" + integrity sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg== + dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -5511,6 +5804,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + env-paths@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -6007,6 +6305,11 @@ express@^4.19.2: utils-merge "1.0.1" vary "~1.1.2" +exsolve@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.0.8.tgz#7f5e34da61cd1116deda5136e62292c096f50613" + integrity sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA== + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -6370,6 +6673,15 @@ fs-extra@^9.0.1: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@~11.3.0: + version "11.3.2" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.2.tgz#c838aeddc6f4a8c74dd15f85e11fe5511bfe02a4" + integrity sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -6857,6 +7169,11 @@ hastscript@^6.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + header-case@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" @@ -7067,6 +7384,11 @@ import-lazy@^3.1.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc" integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ== +import-lazy@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -7260,7 +7582,7 @@ is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.13.0, is-core-module@^2.16.0, is-core-module@^2.8.1: +is-core-module@^2.13.0, is-core-module@^2.16.0, is-core-module@^2.16.1, is-core-module@^2.8.1: version "2.16.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== @@ -7722,6 +8044,11 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +javascript-natural-sort@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" + integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== + jest-worker@^26.3.0: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -7740,7 +8067,7 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jju@^1.4.0: +jju@^1.4.0, jju@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== @@ -7894,6 +8221,11 @@ known-css-properties@^0.36.0: resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.36.0.tgz#5c4365f3c9549ca2e813d2e729e6c47ef6a6cb60" integrity sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA== +kolorist@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + language-subtag-registry@^0.3.20: version "0.3.23" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" @@ -7975,6 +8307,15 @@ loader-runner@^4.2.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== +local-pkg@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-1.1.2.tgz#c03d208787126445303f8161619dc701afa4abb5" + integrity sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A== + dependencies: + mlly "^1.7.4" + pkg-types "^2.3.0" + quansync "^0.2.11" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -8004,6 +8345,11 @@ locate-path@^7.2.0: dependencies: p-locate "^6.0.0" +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -8029,7 +8375,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.21, lodash@^4.17.4: +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.21, lodash@^4.17.4, lodash@~4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8132,6 +8478,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + lru-cache@^7.4.4, lru-cache@^7.5.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" @@ -8832,6 +9185,13 @@ min-indent@^1.0.0, min-indent@^1.0.1: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +minimatch@10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.3.tgz#cf7a0314a16c4d9ab73a7730a0e8e3c3502d47aa" + integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== + dependencies: + "@isaacs/brace-expansion" "^5.0.0" + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -8846,7 +9206,7 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.0, minimatch@^9.0.4: +minimatch@^9.0.0, minimatch@^9.0.3, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== @@ -8896,6 +9256,16 @@ mkdirp@^3.0.1: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== +mlly@^1.7.4: + version "1.8.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.8.0.tgz#e074612b938af8eba1eaf43299cbc89cb72d824e" + integrity sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g== + dependencies: + acorn "^8.15.0" + pathe "^2.0.3" + pkg-types "^1.3.1" + ufo "^1.6.1" + module-details-from-path@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz#b662fdcd93f6c83d3f25289da0ce81c8d9685b94" @@ -8927,6 +9297,11 @@ ms@2.1.3, ms@^2.1.2, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +muggle-string@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328" + integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ== + mute-stream@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" @@ -9502,6 +9877,13 @@ parse-glob@^3.0.4: is-extglob "^1.0.0" is-glob "^2.0.0" +parse-imports-exports@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz#e3fb3b5e264cfb55c25b5dfcbe7f410f8dc4e7af" + integrity sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ== + dependencies: + parse-statements "1.0.11" + parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -9530,6 +9912,11 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== +parse-statements@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/parse-statements/-/parse-statements-1.0.11.tgz#8787c5d383ae5746568571614be72b0689584344" + integrity sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA== + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -9548,6 +9935,11 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" @@ -9633,7 +10025,7 @@ pathe@^1.1.2: resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== -pathe@^2.0.3: +pathe@^2.0.1, pathe@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== @@ -9722,6 +10114,24 @@ pirates@^4.0.1: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== +pkg-types@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" + integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== + dependencies: + confbox "^0.1.8" + mlly "^1.7.4" + pathe "^2.0.1" + +pkg-types@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-2.3.0.tgz#037f2c19bd5402966ff6810e32706558cb5b5726" + integrity sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== + dependencies: + confbox "^0.2.2" + exsolve "^1.0.7" + pathe "^2.0.3" + playwright-core@1.55.1: version "1.55.1" resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.55.1.tgz#5d3bb1846bc4289d364ea1a9dcb33f14545802e9" @@ -10119,6 +10529,11 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" +quansync@^0.2.11: + version "0.2.11" + resolved "https://registry.yarnpkg.com/quansync/-/quansync-0.2.11.tgz#f9c3adda2e1272e4f8cf3f1457b04cbdb4ee692a" + integrity sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -10657,6 +11072,15 @@ resolve@^2.0.0-next.5: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@~1.22.1, resolve@~1.22.2: + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + dependencies: + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" @@ -10909,6 +11333,13 @@ semver@^7.1.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semve resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== +semver@~7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + send@0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" @@ -11340,6 +11771,11 @@ stream-slice@^0.1.2: resolved "https://registry.yarnpkg.com/stream-slice/-/stream-slice-0.1.2.tgz#2dc4f4e1b936fb13f3eb39a2def1932798d07a4b" integrity sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA== +string-argv@~0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: name string-width-cjs version "4.2.3" @@ -11515,7 +11951,7 @@ strip-indent@^4.0.0: dependencies: min-indent "^1.0.1" -strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -11737,7 +12173,7 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: +supports-color@^8.0.0, supports-color@~8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -12108,11 +12544,21 @@ typescript-plugin-css-modules@^5.1.0: stylus "^0.62.0" tsconfig-paths "^4.2.0" +typescript@5.8.2: + version "5.8.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4" + integrity sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ== + typescript@^5.6.2: version "5.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== +ufo@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.6.1.tgz#ac2db1d54614d1b22c1d603e3aef44a85d8f146b" + integrity sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA== + uglify-js@^3.1.4: version "3.19.3" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" @@ -12365,7 +12811,7 @@ upper-case@^2.0.2: dependencies: tslib "^2.0.3" -uri-js@^4.2.2: +uri-js@^4.2.2, uri-js@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== @@ -12511,6 +12957,21 @@ vite-node@3.2.4, vite-node@^3.1.4: pathe "^2.0.3" vite "^5.0.0 || ^6.0.0 || ^7.0.0-0" +vite-plugin-dts@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz#51b60aaaa760d9cf5c2bb3676c69d81910d6b08c" + integrity sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg== + dependencies: + "@microsoft/api-extractor" "^7.50.1" + "@rollup/pluginutils" "^5.1.4" + "@volar/typescript" "^2.4.11" + "@vue/language-core" "2.2.0" + compare-versions "^6.1.1" + debug "^4.4.0" + kolorist "^1.8.0" + local-pkg "^1.0.0" + magic-string "^0.30.17" + vite-tsconfig-paths@^5.0.1: version "5.1.4" resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz#d9a71106a7ff2c1c840c6f1708042f76a9212ed4" @@ -12520,20 +12981,6 @@ vite-tsconfig-paths@^5.0.1: globrex "^0.1.2" tsconfck "^3.0.3" -vite@7.1.7: - version "7.1.7" - resolved "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz#ed3f9f06e21d6574fe1ad425f6b0912d027ffc13" - integrity sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA== - dependencies: - esbuild "^0.25.0" - fdir "^6.5.0" - picomatch "^4.0.3" - postcss "^8.5.6" - rollup "^4.43.0" - tinyglobby "^0.2.15" - optionalDependencies: - fsevents "~2.3.3" - "vite@^5.0.0 || ^6.0.0 || ^7.0.0-0": version "7.0.0" resolved "https://registry.npmjs.org/vite/-/vite-7.0.0.tgz#5675bb4c956dd9da932583628e7758ab09fe761f" @@ -12548,6 +12995,20 @@ vite@7.1.7: optionalDependencies: fsevents "~2.3.3" +vite@^7.2.6: + version "7.2.6" + resolved "https://registry.yarnpkg.com/vite/-/vite-7.2.6.tgz#91e05ba05bc7c667a7645595e21f2a0eb1057631" + integrity sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ== + dependencies: + esbuild "^0.25.0" + fdir "^6.5.0" + picomatch "^4.0.3" + postcss "^8.5.6" + rollup "^4.43.0" + tinyglobby "^0.2.15" + optionalDependencies: + fsevents "~2.3.3" + vitest@3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea" @@ -12577,6 +13038,11 @@ vitest@3.2.4: vite-node "3.2.4" why-is-node-running "^2.3.0" +vscode-uri@^3.0.8: + version "3.1.0" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" + integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== + watchpack@^2.4.1: version "2.4.4" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.4.tgz#473bda72f0850453da6425081ea46fc0d7602947" @@ -12825,6 +13291,11 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yaml@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"