diff --git a/.changeset/dull-breads-start.md b/.changeset/dull-breads-start.md new file mode 100644 index 00000000000..387b53419da --- /dev/null +++ b/.changeset/dull-breads-start.md @@ -0,0 +1,5 @@ +--- +"thirdweb": patch +--- + +Adds paymentMethods prop to BuyWidget, CheckoutWidget, and TransactionWidget to control available payment options. Accepts an array of "crypto" and/or "card" values. diff --git a/apps/playground-web/src/app/connect/pay/components/CodeGen.tsx b/apps/playground-web/src/app/connect/pay/components/CodeGen.tsx index 320bea9502d..d63b58c7f23 100644 --- a/apps/playground-web/src/app/connect/pay/components/CodeGen.tsx +++ b/apps/playground-web/src/app/connect/pay/components/CodeGen.tsx @@ -73,7 +73,7 @@ function Example() { <${componentName} client={client} chain={defineChain(${options.payOptions.buyTokenChain.id})} - amount="${options.payOptions.buyTokenAmount}"${options.payOptions.buyTokenAddress ? `\n\t token="${options.payOptions.buyTokenAddress}"` : ""}${options.payOptions.sellerAddress ? `\n\t seller="${options.payOptions.sellerAddress}"` : ""}${options.payOptions.title ? `\n\t ${options.payOptions.widget === "checkout" ? "name" : "title"}="${options.payOptions.title}"` : ""}${options.payOptions.image ? `\n\t image="${options.payOptions.image}"` : ""}${options.payOptions.description ? `\n\t description="${options.payOptions.description}"` : ""}${ + amount="${options.payOptions.buyTokenAmount}"${options.payOptions.buyTokenAddress ? `\n\t token="${options.payOptions.buyTokenAddress}"` : ""}${options.payOptions.sellerAddress ? `\n\t seller="${options.payOptions.sellerAddress}"` : ""}${options.payOptions.title ? `\n\t ${options.payOptions.widget === "checkout" ? "name" : "title"}="${options.payOptions.title}"` : ""}${options.payOptions.image ? `\n\t image="${options.payOptions.image}"` : ""}${options.payOptions.description ? `\n\t description="${options.payOptions.description}"` : ""}${options.payOptions.paymentMethods && options.payOptions.paymentMethods.length > 0 ? `\n\t paymentMethods={${JSON.stringify(options.payOptions.paymentMethods)}}` : ""}${ options.payOptions.widget === "transaction" ? `\n\t transaction={claimTo({ contract: nftContract, diff --git a/apps/playground-web/src/app/connect/pay/components/types.ts b/apps/playground-web/src/app/connect/pay/components/types.ts index f784cef8b06..033cb355033 100644 --- a/apps/playground-web/src/app/connect/pay/components/types.ts +++ b/apps/playground-web/src/app/connect/pay/components/types.ts @@ -1,5 +1,6 @@ -import type { Address, Chain } from "thirdweb"; +import type { Chain } from "thirdweb/chains"; import type { ThemeOverrides } from "thirdweb/react"; +import type { Address } from "thirdweb/utils"; export type BridgeComponentsPlaygroundOptions = { theme: { @@ -22,5 +23,7 @@ export type BridgeComponentsPlaygroundOptions = { // transaction mode options transactionData?: string; // Simplified for demo; could be more complex in real implementation + + paymentMethods: ("crypto" | "card")[]; }; }; diff --git a/apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx b/apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx index bce6ea14a98..5fb7f46ecad 100644 --- a/apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx +++ b/apps/playground-web/src/app/connect/pay/embed/LeftSection.tsx @@ -13,6 +13,7 @@ import { useId, useState } from "react"; import type { Address } from "thirdweb"; import { defineChain } from "thirdweb/chains"; import { CustomRadioGroup } from "@/components/ui/CustomRadioGroup"; +import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { cn } from "../../../../lib/utils"; @@ -53,6 +54,8 @@ export function LeftSection(props: { const modalTitleIconId = useId(); const modalDescriptionId = useId(); const themeId = useId(); + const cryptoPaymentId = useId(); + const cardPaymentId = useId(); return (
@@ -161,6 +164,65 @@ export function LeftSection(props: { />
+ + {/* Payment Methods */} +
+ +
+
+ { + setOptions((v) => ({ + ...v, + payOptions: { + ...v.payOptions, + paymentMethods: checked + ? [ + ...v.payOptions.paymentMethods.filter( + (m) => m !== "crypto", + ), + "crypto", + ] + : v.payOptions.paymentMethods.filter( + (m) => m !== "crypto", + ), + }, + })); + }} + /> + +
+
+ { + setOptions((v) => ({ + ...v, + payOptions: { + ...v.payOptions, + paymentMethods: checked + ? [ + ...v.payOptions.paymentMethods.filter( + (m) => m !== "card", + ), + "card", + ] + : v.payOptions.paymentMethods.filter( + (m) => m !== "card", + ), + }, + })); + }} + /> + +
+
+
@@ -249,6 +311,65 @@ export function LeftSection(props: { /> + + {/* Payment Methods */} +
+ +
+
+ { + setOptions((v) => ({ + ...v, + payOptions: { + ...v.payOptions, + paymentMethods: checked + ? [ + ...v.payOptions.paymentMethods.filter( + (m) => m !== "crypto", + ), + "crypto", + ] + : v.payOptions.paymentMethods.filter( + (m) => m !== "crypto", + ), + }, + })); + }} + /> + +
+
+ { + setOptions((v) => ({ + ...v, + payOptions: { + ...v.payOptions, + paymentMethods: checked + ? [ + ...v.payOptions.paymentMethods.filter( + (m) => m !== "card", + ), + "card", + ] + : v.payOptions.paymentMethods.filter( + (m) => m !== "card", + ), + }, + })); + }} + /> + +
+
+
diff --git a/apps/playground-web/src/app/connect/pay/embed/RightSection.tsx b/apps/playground-web/src/app/connect/pay/embed/RightSection.tsx index 01a7be57a62..9419111cbc5 100644 --- a/apps/playground-web/src/app/connect/pay/embed/RightSection.tsx +++ b/apps/playground-web/src/app/connect/pay/embed/RightSection.tsx @@ -60,6 +60,7 @@ export function RightSection(props: { client={THIRDWEB_CLIENT} description={props.options.payOptions.description} image={props.options.payOptions.image} + paymentMethods={props.options.payOptions.paymentMethods} theme={themeObj} title={props.options.payOptions.title} tokenAddress={props.options.payOptions.buyTokenAddress} @@ -81,6 +82,7 @@ export function RightSection(props: { getDefaultImage(props.options.theme.type) } name={props.options.payOptions.title || "Your Product Name"} + paymentMethods={props.options.payOptions.paymentMethods} presetOptions={[1, 2, 3]} seller={props.options.payOptions.sellerAddress} theme={themeObj} @@ -95,6 +97,7 @@ export function RightSection(props: { client={THIRDWEB_CLIENT} description={props.options.payOptions.description} image={props.options.payOptions.image} + paymentMethods={props.options.payOptions.paymentMethods} theme={themeObj} title={props.options.payOptions.title} transaction={claimTo({ diff --git a/apps/playground-web/src/app/connect/pay/embed/page.tsx b/apps/playground-web/src/app/connect/pay/embed/page.tsx index b97ea46a286..67f24686e98 100644 --- a/apps/playground-web/src/app/connect/pay/embed/page.tsx +++ b/apps/playground-web/src/app/connect/pay/embed/page.tsx @@ -13,6 +13,7 @@ const defaultConnectOptions: BridgeComponentsPlaygroundOptions = { buyTokenChain: arbitrum, description: "", image: "", + paymentMethods: ["crypto", "card"], sellerAddress: "0x0000000000000000000000000000000000000000", title: "", transactionData: "", diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx index 96ba777db88..67974983774 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/BridgeOrchestrator.tsx @@ -113,6 +113,12 @@ export interface BridgeOrchestratorProps { * Quick buy amounts */ presetOptions: [number, number, number] | undefined; + + /** + * Allowed payment methods + * @default ["crypto", "card"] + */ + paymentMethods?: ("crypto" | "card")[]; } export function BridgeOrchestrator({ @@ -127,6 +133,7 @@ export function BridgeOrchestrator({ purchaseData, paymentLinkId, presetOptions, + paymentMethods = ["crypto", "card"], }: BridgeOrchestratorProps) { // Initialize adapters const adapters = useMemo( @@ -270,6 +277,7 @@ export function BridgeOrchestrator({ }} onError={handleError} onPaymentMethodSelected={handlePaymentMethodSelected} + paymentMethods={paymentMethods} receiverAddress={state.context.receiverAddress} /> )} diff --git a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx index 11a99b29ff7..dcb51dd8e79 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/BuyWidget.tsx @@ -160,6 +160,12 @@ export type BuyWidgetProps = { * @hidden */ paymentLinkId?: string; + + /** + * Allowed payment methods + * @default ["crypto", "card"] + */ + paymentMethods?: ("crypto" | "card")[]; }; // Enhanced UIOptions to handle unsupported token state @@ -378,6 +384,7 @@ export function BuyWidget(props: BuyWidgetProps) { props.onError?.(err); }} paymentLinkId={props.paymentLinkId} + paymentMethods={props.paymentMethods} presetOptions={props.presetOptions} purchaseData={props.purchaseData} receiverAddress={undefined} diff --git a/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx index b25c19f0618..766b1627f8c 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/CheckoutWidget.tsx @@ -166,6 +166,12 @@ export type CheckoutWidgetProps = { * @hidden */ paymentLinkId?: string; + + /** + * Allowed payment methods + * @default ["crypto", "card"] + */ + paymentMethods?: ("crypto" | "card")[]; }; // Enhanced UIOptions to handle unsupported token state @@ -341,6 +347,7 @@ export function CheckoutWidget(props: CheckoutWidgetProps) { props.onError?.(err); }} paymentLinkId={props.paymentLinkId} + paymentMethods={props.paymentMethods} presetOptions={props.presetOptions} purchaseData={props.purchaseData} receiverAddress={props.seller} diff --git a/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx b/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx index 293bbcc4148..ca94be9a1d2 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/TransactionWidget.tsx @@ -169,6 +169,12 @@ export type TransactionWidgetProps = { * @hidden */ paymentLinkId?: string; + + /** + * Allowed payment methods + * @default ["crypto", "card"] + */ + paymentMethods?: ("crypto" | "card")[]; }; // Enhanced UIOptions to handle unsupported token state @@ -400,6 +406,7 @@ export function TransactionWidget(props: TransactionWidgetProps) { props.onError?.(err); }} paymentLinkId={props.paymentLinkId} + paymentMethods={props.paymentMethods} presetOptions={props.presetOptions} purchaseData={props.purchaseData} receiverAddress={undefined} diff --git a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx index e193d54cbdd..0ccafe8b1c6 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/PaymentSelection.tsx @@ -71,6 +71,12 @@ export interface PaymentSelectionProps { * Whether to include the destination token in the payment methods */ includeDestinationToken?: boolean; + + /** + * Allowed payment methods + * @default ["crypto", "card"] + */ + paymentMethods?: ("crypto" | "card")[]; } type Step = @@ -90,6 +96,7 @@ export function PaymentSelection({ connectOptions, connectLocale, includeDestinationToken, + paymentMethods = ["crypto", "card"], }: PaymentSelectionProps) { const connectedWallets = useConnectedWallets(); const activeWallet = useActiveWallet(); @@ -115,7 +122,7 @@ export function PaymentSelection({ ? currentStep.selectedWallet : activeWallet; const { - data: paymentMethods, + data: suitableTokenPaymentMethods, isLoading: paymentMethodsLoading, error: paymentMethodsError, } = usePaymentMethods({ @@ -248,6 +255,7 @@ export function PaymentSelection({ onConnectWallet={handleConnectWallet} onFiatSelected={handleFiatSelected} onWalletSelected={handleWalletSelected} + paymentMethods={paymentMethods} /> )} @@ -261,7 +269,7 @@ export function PaymentSelection({ destinationToken={destinationToken} onBack={handleBackToWalletSelection} onPaymentMethodSelected={handlePaymentMethodSelected} - paymentMethods={paymentMethods} + paymentMethods={suitableTokenPaymentMethods} paymentMethodsLoading={paymentMethodsLoading} /> )} diff --git a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/WalletFiatSelection.tsx b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/WalletFiatSelection.tsx index c224dad027d..b75dfcf92e4 100644 --- a/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/WalletFiatSelection.tsx +++ b/packages/thirdweb/src/react/web/ui/Bridge/payment-selection/WalletFiatSelection.tsx @@ -21,6 +21,7 @@ interface WalletFiatSelectionProps { onWalletSelected: (wallet: Wallet) => void; onFiatSelected: () => void; onConnectWallet: () => void; + paymentMethods?: ("crypto" | "card")[]; } export function WalletFiatSelection({ @@ -29,144 +30,152 @@ export function WalletFiatSelection({ onWalletSelected, onFiatSelected, onConnectWallet, + paymentMethods = ["crypto", "card"], }: WalletFiatSelectionProps) { const theme = useCustomTheme(); return ( <> - - Pay with Crypto - - - {/* Connected Wallets */} - {connectedWallets.length > 0 && ( + {paymentMethods.includes("crypto") && ( <> - - {connectedWallets.map((wallet) => { - const account = wallet.getAccount(); - if (!account?.address) { - return null; - } - return ( - - ); - })} - - - - )} + + Pay with Crypto + + + {/* Connected Wallets */} + {connectedWallets.length > 0 && ( + <> + + {connectedWallets.map((wallet) => { + const account = wallet.getAccount(); + if (!account?.address) { + return null; + } + return ( + + ); + })} + + + + )} - {/* Connect Another Wallet */} - + + + + + + + Connect Another Wallet + + + Use a different wallet to pay + + + + + + )} - + {paymentMethods.includes("card") && ( + <> + - {/* Pay with Debit Card */} - - Pay with Fiat - + + Pay with Card + - + - + + + )} ); } diff --git a/packages/thirdweb/src/react/web/ui/PayEmbed.tsx b/packages/thirdweb/src/react/web/ui/PayEmbed.tsx index a43d3346d34..dc1031ee231 100644 --- a/packages/thirdweb/src/react/web/ui/PayEmbed.tsx +++ b/packages/thirdweb/src/react/web/ui/PayEmbed.tsx @@ -238,7 +238,6 @@ export type PayEmbedProps = { * }} * /> * ``` - * * You can also handle ERC20 payments by passing `erc20value` to your transaction: * * ```tsx @@ -357,6 +356,13 @@ export function PayEmbed(props: PayEmbedProps) { chain={props.payOptions.prefillBuy.chain} client={props.client} onSuccess={() => props.payOptions?.onPurchaseSuccess?.()} + paymentMethods={ + props.payOptions?.buyWithFiat === false + ? ["crypto"] + : props.payOptions?.buyWithCrypto === false + ? ["card"] + : ["crypto", "card"] + } theme={theme} title={metadata?.name || "Buy"} tokenAddress={ @@ -376,6 +382,11 @@ export function PayEmbed(props: PayEmbedProps) { image={metadata?.image} name={metadata?.name || "Checkout"} onSuccess={() => props.payOptions?.onPurchaseSuccess?.()} + paymentMethods={ + props.payOptions?.buyWithFiat === false + ? ["crypto"] + : ["crypto", "card"] + } seller={props.payOptions.paymentInfo.sellerAddress as Address} theme={theme} tokenAddress={ @@ -392,6 +403,11 @@ export function PayEmbed(props: PayEmbedProps) { description={metadata?.description} image={metadata?.image} onSuccess={() => props.payOptions?.onPurchaseSuccess?.()} + paymentMethods={ + props.payOptions?.buyWithFiat === false + ? ["crypto"] + : ["crypto", "card"] + } theme={theme} title={metadata?.name} transaction={props.payOptions.transaction} diff --git a/packages/thirdweb/src/wallets/in-app/core/authentication/backend.test.ts b/packages/thirdweb/src/wallets/in-app/core/authentication/backend.test.ts index 5862ce5e0ca..f34c044b3f8 100644 --- a/packages/thirdweb/src/wallets/in-app/core/authentication/backend.test.ts +++ b/packages/thirdweb/src/wallets/in-app/core/authentication/backend.test.ts @@ -49,6 +49,7 @@ describe("backendAuthenticate", () => { // Mock failed fetch response const mockFetch = vi.fn().mockResolvedValue({ ok: false, + text: () => Promise.resolve("Failed to generate backend account"), }); // Mock dependencies diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e439e79f010..f4b1089494f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -411,8 +411,6 @@ importers: specifier: 5.8.3 version: 5.8.3 - apps/login: {} - apps/nebula: dependencies: '@hookform/resolvers': @@ -7612,7 +7610,6 @@ packages: '@walletconnect/modal@2.7.0': resolution: {integrity: sha512-RQVt58oJ+rwqnPcIvRFeMGKuXb9qkgSmwz4noF8JZGUym3gUAzVs+uW2NQ1Owm9XOJAV+sANrtJ+VoVq1ftElw==} - deprecated: Please follow the migration guide on https://docs.reown.com/appkit/upgrade/wcm '@walletconnect/react-native-compat@2.17.3': resolution: {integrity: sha512-lHKwXKoB0rdDH1ukxUx7o86xosWbttWIHYMZ8tgAQC1k9VH3CZZCoBcHOAAX8iBzyb0n0UP3/9zRrOcJE5nz7Q==} @@ -29410,8 +29407,8 @@ snapshots: '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.8.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) eslint-plugin-react: 7.37.5(eslint@8.57.0) eslint-plugin-react-hooks: 5.2.0(eslint@8.57.0) @@ -29430,7 +29427,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.0): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1(supports-color@8.1.1) @@ -29441,7 +29438,7 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.9.0 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -29466,18 +29463,18 @@ snapshots: - bluebird - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.8.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -29488,7 +29485,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.8.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3