[Engine] Add FAILED status to execution result and update error handling (#7065)

This commit is contained in:
Joaquim Verges
2025-05-16 03:34:27 -07:00
committed by GitHub
parent 05519fcdde
commit 61152dd098
17 changed files with 228 additions and 162 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---
Return timestamps in Engine.getTransactionStatus()
@@ -32,6 +32,10 @@ type ExecutionResult4337Serialized =
| {
status: "QUEUED";
}
| {
status: "FAILED";
error: string;
}
| {
status: "SUBMITTED";
monitoringStatus: "WILL_MONITOR" | "CANNOT_MONITOR";
@@ -335,7 +335,6 @@ export async function createManagementAccessToken(props: {
});
if (res.success) {
const data = res.data;
// store the management access token in the project
await updateProjectClient(
{
projectId: props.project.id,
@@ -354,8 +353,9 @@ export async function createManagementAccessToken(props: {
],
},
);
return res;
}
return res;
throw new Error(`Failed to create management access token: ${res.error}`);
}
export function maskSecret(secret: string) {
@@ -36,12 +36,17 @@ export function TransactionDetailsUI({
transactionHash,
confirmedAt,
createdAt,
errorMessage,
executionParams,
executionResult,
} = transaction;
const status = executionResult?.status as keyof typeof statusDetails;
const errorMessage =
executionResult && "error" in executionResult
? executionResult.error
: executionResult && "revertData" in executionResult
? executionResult.revertData?.revertReason
: null;
const chain = chainId ? idToChain.get(Number.parseInt(chainId)) : undefined;
const explorer = chain?.explorers?.[0];
-1
View File
@@ -28,7 +28,6 @@
"@radix-ui/react-switch": "^1.2.2",
"@radix-ui/react-tooltip": "1.2.3",
"@tanstack/react-query": "5.74.4",
"@thirdweb-dev/engine": "0.0.19",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "4.1.0",
@@ -9,16 +9,64 @@ export function useEngineTxStatus(queueId: string | undefined) {
if (!queueId) throw new Error("No queue ID provided");
const res = await get_engine_tx_status(queueId);
const txStatus: EngineTxStatus = {
queueId: queueId,
status: res.result.status,
chainId: res.result.chainId,
transactionHash: res.result.transactionHash,
queuedAt: res.result.queuedAt,
sentAt: res.result.sentAt,
minedAt: res.result.minedAt,
cancelledAt: res.result.cancelledAt,
};
let txStatus: EngineTxStatus;
switch (res.status) {
case "QUEUED": {
txStatus = {
queueId: queueId,
status: "queued",
chainId: res.chain.id.toString(),
transactionHash: null,
queuedAt: res.createdAt,
sentAt: null,
minedAt: null,
cancelledAt: null,
};
break;
}
case "SUBMITTED": {
txStatus = {
queueId: queueId,
status: "sent",
chainId: res.chain.id.toString(),
transactionHash: null,
queuedAt: res.createdAt,
sentAt: res.createdAt,
minedAt: null,
cancelledAt: null,
};
break;
}
case "CONFIRMED": {
txStatus = {
queueId: queueId,
status: "mined",
chainId: res.chain.id.toString(),
transactionHash: res.transactionHash,
queuedAt: res.createdAt,
sentAt: res.confirmedAt,
minedAt: res.confirmedAt,
cancelledAt: null,
};
break;
}
case "FAILED": {
txStatus = {
queueId: queueId,
status: "errored",
chainId: res.chain.id.toString(),
transactionHash: null,
queuedAt: res.createdAt,
sentAt: null,
minedAt: null,
cancelledAt: res.cancelledAt,
};
break;
}
default: {
throw new Error(`Unknown engine tx status: ${res}`);
}
}
return txStatus;
},
+61 -35
View File
@@ -1,12 +1,18 @@
"use server";
import { Engine } from "@thirdweb-dev/engine";
import { Engine, defineChain, encode, getContract } from "thirdweb";
import { multicall } from "thirdweb/extensions/common";
import * as ERC20 from "thirdweb/extensions/erc20";
import * as ERC1155 from "thirdweb/extensions/erc1155";
import { THIRDWEB_CLIENT } from "../../lib/client";
const BACKEND_WALLET_ADDRESS = process.env.ENGINE_BACKEND_WALLET as string;
const ENGINE_VAULT_ACCESS_TOKEN = process.env
.ENGINE_VAULT_ACCESS_TOKEN as string;
const engine = new Engine({
url: process.env.ENGINE_URL as string,
accessToken: process.env.ENGINE_ACCESS_TOKEN as string,
const serverWallet = Engine.serverWallet({
address: BACKEND_WALLET_ADDRESS,
client: THIRDWEB_CLIENT,
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
});
export async function airdrop_tokens_with_engine(params: {
@@ -17,20 +23,37 @@ export async function airdrop_tokens_with_engine(params: {
amount: string;
}[];
}) {
const res = await engine.erc20.mintBatchTo(
params.chainId.toString(),
params.contractAddress,
BACKEND_WALLET_ADDRESS,
{
data: params.receivers,
},
const contract = getContract({
address: params.contractAddress,
chain: defineChain(params.chainId),
client: THIRDWEB_CLIENT,
});
const data = await Promise.all(
params.receivers.map((receiver) =>
encode(
ERC20.mintTo({
contract,
to: receiver.toAddress,
amount: receiver.amount,
}),
),
),
);
const tx = multicall({
contract,
data,
});
return res.result;
const res = await serverWallet.enqueueTransaction({ transaction: tx });
return res.transactionId;
}
export async function get_engine_tx_status(queueId: string) {
const status = await engine.transaction.status(queueId);
const status = await Engine.getTransactionStatus({
client: THIRDWEB_CLIENT,
transactionId: queueId,
});
return status;
}
@@ -49,17 +72,19 @@ type MintNFTParams = {
};
export async function mint_erc1155_nft_with_engine(params: MintNFTParams) {
const res = await engine.erc1155.mintTo(
params.chainId.toString(),
params.contractAddress,
BACKEND_WALLET_ADDRESS,
{
receiver: params.toAddress,
metadataWithSupply: params.metadataWithSupply,
},
);
const tx = ERC1155.mintTo({
contract: getContract({
address: params.contractAddress,
chain: defineChain(params.chainId),
client: THIRDWEB_CLIENT,
}),
nft: params.metadataWithSupply.metadata,
to: params.toAddress,
supply: BigInt(params.metadataWithSupply.supply),
});
const res = await serverWallet.enqueueTransaction({ transaction: tx });
return res.result;
return res.transactionId;
}
type ClaimNFTParams = {
@@ -71,16 +96,17 @@ type ClaimNFTParams = {
};
export async function claim_erc1155_nft_with_engine(params: ClaimNFTParams) {
const res = await engine.erc1155.claimTo(
params.chainId.toString(),
params.contractAddress,
BACKEND_WALLET_ADDRESS,
{
receiver: params.receiverAddress,
quantity: params.quantity.toString(),
tokenId: params.tokenId,
},
);
const tx = ERC1155.claimTo({
contract: getContract({
address: params.contractAddress,
chain: defineChain(params.chainId),
client: THIRDWEB_CLIENT,
}),
to: params.receiverAddress,
tokenId: BigInt(params.tokenId),
quantity: BigInt(params.quantity),
});
const res = await serverWallet.enqueueTransaction({ transaction: tx });
return res.result;
return res.transactionId;
}
@@ -34,49 +34,49 @@ export function AirdropCode() {
}
const engineAirdropSendCode = `\
const chainId = ${airdropExample.chainId};
const contractAddress = "${airdropExample.contractAddress}";
const addresses = ${JSON.stringify(
airdropExample.receivers.map((x) => ({
address: x.toAddress,
quantity: x.amount,
recipient: x.toAddress,
amount: x.amount,
})),
null,
2,
)};
const url = \`\${YOUR_ENGINE_URL}\/contract/\${chainId}/\${contractAddress}\/erc1155\/airdrop\`;
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_SECRET_TOKEN",
"Content-Type": "application/json",
"X-Backend-Wallet-Address": "YOUR_BACKEND_WALLET_ADDRESS",
},
body: JSON.stringify({ addresses }),
const contract = getContract({
address: ${airdropExample.contractAddress},
chain: defineChain(${airdropExample.chainId}),
client: THIRDWEB_CLIENT,
});
const data = await response.json();
const queueId = data.queueId;
const transaction = airdropERC20({
contract,
tokenAddress: ${airdropExample.contractAddress},
contents: addresses,
});
const serverWallet = Engine.serverWallet({
address: BACKEND_WALLET_ADDRESS,
client: THIRDWEB_CLIENT,
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
});
const { transactionId } = await serverWallet.enqueueTransaction({ transaction });
`;
const engineAirdropGetStatus = `\
function getEngineTxStatus(queueId: string) {
const url = \`\${YOUR_ENGINE_URL}\/transaction/\${queueId}\`;
const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": "Bearer YOUR_SECRET_TOKEN",
},
});
const data = await response.json();
return data.result;
}
// you can keep polling for the status until you get a status of either "mined" or "errored" or "cancelled"
const result = await getEngineTxStatus(queueId);
const result = await Engine.getTransactionStatus({
client: THIRDWEB_CLIENT,
transactionId: transactionId,
});
console.log(result.status);
// or wait for the transaction to be mined (polls status until it's mined)
const result = await Engine.waitForTransactionHash({
client: THIRDWEB_CLIENT,
transactionId: transactionId,
});
console.log(result.transactionHash);
`;
@@ -32,10 +32,10 @@ export function EngineAirdropPreview() {
const res = await airdropMutation.mutateAsync();
updateEngineTxStatus({
chainId: airdropExample.chainId,
queueId: res.queueId,
queueId: res,
});
setQueueId(res.queueId);
setQueueId(res);
};
return (
@@ -1,5 +1,5 @@
export const airdropExample = {
contractAddress: "0xcB30dB8FB977e8b27ae34c86aF16C4F5E428c0bE",
contractAddress: "0x6E238275023A2575136CF60f655B6B2C0C58b4ac",
chainId: 84532,
chainName: "Base Sepolia",
chainExplorer: "https://base-sepolia.blockscout.com",
@@ -13,7 +13,7 @@ export function MintCode() {
<div className="h-4" />
<h3 className="mb-2 font-semibold text-xl tracking-tight">
Send Transaction Request to Mint NFTs
Send Transaction Request to Mint Dynamic NFTs
</h3>
<Code code={engineMintCode} lang="typescript" />
@@ -36,48 +36,45 @@ export function MintCode() {
const engineMintCode = `\
const chainId = ${mintExample.chainId};
const contractAddress = "${mintExample.contractAddress}";
const url = \`\${YOUR_ENGINE_URL}\/contract/\${chainId}/\${contractAddress}\/erc1155\/mint-to\`;
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_SECRET_TOKEN",
"Content-Type": "application/json",
"X-Backend-Wallet-Address": "YOUR_BACKEND_WALLET_ADDRESS",
},
body: JSON.stringify({
receiver: "0x....",
metadataWithSupply: {
metadata: {
name: "...",
description: "...",
image: "...", // ipfs or https link to your asset
},
supply: "1",
},
})
const contract = getContract({
address: ${mintExample.contractAddress},
chain: defineChain(${mintExample.chainId}),
client: THIRDWEB_CLIENT,
});
const data = await response.json();
console.log(data.queueId);
const transaction = mintTo({
contract,
to: "0x....",
nft: {
name: "...",
description: "...",
image: "...", // ipfs or https link to your asset
},
supply: "1",
});
const serverWallet = Engine.serverWallet({
address: BACKEND_WALLET_ADDRESS,
client: THIRDWEB_CLIENT,
vaultAccessToken: ENGINE_VAULT_ACCESS_TOKEN,
});
const { transactionId } = await serverWallet.enqueueTransaction({ transaction });
`;
const getEngineStatusCode = `\
function getEngineTxStatus(queueId: string) {
const url = \`\${YOUR_ENGINE_URL}\/transaction/\${queueId}\`;
const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": "Bearer YOUR_SECRET_TOKEN",
},
});
const data = await response.json();
return data.result;
}
// you can keep polling for the status until you get a status of either "mined" or "errored" or "cancelled"
const result = await getEngineTxStatus(queueId);
const result = await Engine.getTransactionStatus({
client: THIRDWEB_CLIENT,
transactionId: transactionId,
});
console.log(result.status);
// or wait for the transaction to be mined (polls status until it's mined)
const result = await Engine.waitForTransactionHash({
client: THIRDWEB_CLIENT,
transactionId: transactionId,
});
console.log(result.transactionHash);
`;
@@ -231,10 +231,10 @@ export function EngineMintPreview() {
});
// optimistic update
queryClient.setQueryData(["engineTxStatus", result.queueId], {
queryClient.setQueryData(["engineTxStatus", result], {
status: "queued",
chainId: mintExample.chainId.toString(),
queueId: result.queueId,
queueId: result,
transactionHash: null,
queuedAt: new Date().toISOString(),
sentAt: null,
@@ -242,7 +242,7 @@ export function EngineMintPreview() {
cancelledAt: null,
} satisfies EngineTxStatus);
setQueueId(result.queueId);
setQueueId(result);
};
return (
@@ -1,5 +1,5 @@
export const mintExample = {
contractAddress: "0x8CD193648f5D4E8CD9fD0f8d3865052790A680f6",
contractAddress: "0x8a4E14591088bBce4a4Dcfa677C1af78d336d6FB",
chainId: 84532,
chainName: "Base Sepolia",
chainExplorer: "https://base-sepolia.blockscout.com",
@@ -7,7 +7,7 @@ export default function Page() {
return (
<ThirdwebProvider>
<PageLayout
title="Mint NFTs"
title="Mint Dynamic NFTs"
description={
<>
Allow your users to mint new tokens into any given contract. You
@@ -71,7 +71,7 @@ export function EngineWebhooksPreview() {
async function onSubmit(values: z.infer<typeof formSchema>) {
const res = await claimMutation.mutateAsync(values.receiverAddress);
setQueueId(res.queueId);
setQueueId(res);
}
return (
@@ -49,6 +49,9 @@ export type ExecutionResult = Prettify<
chain: Chain;
from: string | undefined;
id: string;
createdAt: string;
confirmedAt: string | null;
cancelledAt: string | null;
}
>;
@@ -105,6 +108,9 @@ export async function getTransactionStatus(args: {
const executionResult = data.executionResult as ExecutionResult4337Serialized;
return {
...executionResult,
createdAt: data.createdAt,
confirmedAt: data.confirmedAt,
cancelledAt: data.cancelledAt,
chain: getCachedChain(Number(data.chainId)),
from: data.from ?? undefined,
id: data.id,
+11 -35
View File
@@ -219,7 +219,7 @@ importers:
version: 0.4.6([email protected]([email protected]))([email protected])
nextjs-toploader:
specifier: ^1.6.12
version: 1.6.12([email protected](@babel/[email protected])(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
version: 1.6.12([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
nuqs:
specifier: ^2.4.3
version: 2.4.3([email protected](@babel/[email protected])(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected])
@@ -415,7 +415,7 @@ importers:
version: 5.50.5(@types/[email protected])([email protected])
next-sitemap:
specifier: ^4.2.3
version: 4.2.3([email protected](@babel/[email protected])(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))
version: 4.2.3([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))
postcss:
specifier: 8.5.3
version: 8.5.3
@@ -561,9 +561,6 @@ importers:
'@tanstack/react-query':
specifier: 5.74.4
version: 5.74.4([email protected])
'@thirdweb-dev/engine':
specifier: 0.0.19
version: 0.0.19
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -1183,7 +1180,7 @@ importers:
version: 3.2.6([email protected])([email protected]([email protected])([email protected])([email protected]))
'@codspeed/vitest-plugin':
specifier: 4.0.1
version: 4.0.1([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))([email protected])
version: 4.0.1([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@types/[email protected])(@types/[email protected])(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])([email protected])([email protected]))
'@coinbase/wallet-mobile-sdk':
specifier: 1.1.2
version: 1.1.2([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))([email protected])([email protected])([email protected])([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])([email protected]))([email protected](@babel/[email protected])(@babel/[email protected](@babel/[email protected]))(@types/[email protected])([email protected])([email protected])([email protected]))([email protected])
@@ -1243,7 +1240,7 @@ importers:
version: 4.4.1([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
'@vitest/coverage-v8':
specifier: 3.1.2
version: 3.1.2([email protected])
version: 3.1.2([email protected](@types/[email protected])(@types/[email protected])(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])([email protected])([email protected]))
'@vitest/ui':
specifier: 3.1.2
version: 3.1.2([email protected])
@@ -6548,9 +6545,6 @@ packages:
peerDependencies:
'@testing-library/dom': '>=7.21.4'
'@thirdweb-dev/[email protected]':
resolution: {integrity: sha512-Gseb0+enmAWnc6T2zX6TnpAy/HP29J5HHOilpKQJ26DI2O+ivQ/m1YrYrIKoye+MIhSAVB5ItQNKxbvuhizVVQ==}
'@tootallnate/[email protected]':
resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
@@ -18431,7 +18425,7 @@ snapshots:
transitivePeerDependencies:
- debug
'@codspeed/[email protected]([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))([email protected])':
'@codspeed/[email protected]([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@types/[email protected])(@types/[email protected])(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])([email protected])([email protected]))':
dependencies:
'@codspeed/core': 4.0.1
vite: 6.3.4(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
@@ -24296,8 +24290,6 @@ snapshots:
dependencies:
'@testing-library/dom': 10.4.0
'@thirdweb-dev/[email protected]': {}
'@tootallnate/[email protected]': {}
'@tree-sitter-grammars/[email protected]([email protected])':
@@ -24935,7 +24927,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@vitest/[email protected]([email protected])':
'@vitest/[email protected]([email protected](@types/[email protected])(@types/[email protected])(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected]))([email protected])([email protected])([email protected]))':
dependencies:
'@ampproject/remapping': 2.3.0
'@bcoe/v8-coverage': 1.0.2
@@ -28035,7 +28027,7 @@ snapshots:
eslint: 9.24.0([email protected])
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1([email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected])))([email protected]([email protected]))
eslint-plugin-import: 2.31.0(@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected]))
eslint-plugin-import: 2.31.0(@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))
eslint-plugin-jsx-a11y: 6.10.2([email protected]([email protected]))
eslint-plugin-react: 7.37.5([email protected]([email protected]))
eslint-plugin-react-hooks: 5.2.0([email protected]([email protected]))
@@ -28065,7 +28057,7 @@ snapshots:
tinyglobby: 0.2.13
unrs-resolver: 1.6.3
optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected]))
eslint-plugin-import: 2.31.0(@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))
transitivePeerDependencies:
- supports-color
@@ -28105,7 +28097,7 @@ snapshots:
- bluebird
- supports-color
[email protected](@typescript-eslint/[email protected]([email protected])([email protected]))([email protected])([email protected])([email protected]):
[email protected](@typescript-eslint/[email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected]))([email protected]):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -28138,7 +28130,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/[email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])
eslint-module-utils: 2.12.0(@typescript-eslint/[email protected]([email protected])([email protected]))([email protected])([email protected]([email protected])([email protected]))([email protected])
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -28156,7 +28148,7 @@ snapshots:
- eslint-import-resolver-webpack
- supports-color
[email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])):
[email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected])):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.8
@@ -32132,14 +32124,6 @@ snapshots:
react: 19.1.0
react-dom: 19.1.0([email protected])
[email protected]([email protected](@babel/[email protected])(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected])):
dependencies:
'@corex/deepmerge': 4.0.43
'@next/env': 13.5.8
fast-glob: 3.3.3
minimist: 1.2.8
next: 15.3.1(@babel/[email protected])(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected])
[email protected]([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected])):
dependencies:
'@corex/deepmerge': 4.0.43
@@ -32180,14 +32164,6 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
[email protected]([email protected](@babel/[email protected])(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected]):
dependencies:
next: 15.3.1(@babel/[email protected])(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected])
nprogress: 0.2.0
prop-types: 15.8.1
react: 19.1.0
react-dom: 19.1.0([email protected])
[email protected]([email protected](@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected]):
dependencies:
next: 15.3.1(@babel/[email protected])(@opentelemetry/[email protected])(@playwright/[email protected])([email protected]([email protected]))([email protected])