diff --git a/.gitignore b/.gitignore index f8d59d3bcb9e..d11873de820e 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ tmp/ *.fls *.gz *.pdf +*.wal .coverage diff --git a/.next/cache/.previewinfo b/.next/cache/.previewinfo new file mode 100644 index 000000000000..f27cde6defe7 --- /dev/null +++ b/.next/cache/.previewinfo @@ -0,0 +1 @@ +{"previewModeId":"8d02f918265600247c8a52876579796b","previewModeSigningKey":"59c00acda990d004aa4aff9a1a3bf2464008e940a4edf3c3c9ae1c65a9cbdb16","previewModeEncryptionKey":"19731b8c0adf2ca885f60731e34005bc4ce763ba4b83cfcf9adcb8b7eed06f18","expireAt":1776977961683} \ No newline at end of file diff --git a/.next/cache/.rscinfo b/.next/cache/.rscinfo new file mode 100644 index 000000000000..370afb9bac7d --- /dev/null +++ b/.next/cache/.rscinfo @@ -0,0 +1 @@ +{"encryption.key":"wr/9ixE7UKgiIj9FzMyXe31u6plQYKhv0f0MBhqcm74=","encryption.expire_at":1776977961677} \ No newline at end of file diff --git a/.next/diagnostics/build-diagnostics.json b/.next/diagnostics/build-diagnostics.json new file mode 100644 index 000000000000..49f364754787 --- /dev/null +++ b/.next/diagnostics/build-diagnostics.json @@ -0,0 +1,6 @@ +{ + "buildStage": "compile", + "buildOptions": { + "useBuildWorker": "true" + } +} \ No newline at end of file diff --git a/.next/diagnostics/framework.json b/.next/diagnostics/framework.json new file mode 100644 index 000000000000..33dbec331303 --- /dev/null +++ b/.next/diagnostics/framework.json @@ -0,0 +1 @@ +{"name":"Next.js","version":"16.2.3"} \ No newline at end of file diff --git a/.next/package.json b/.next/package.json new file mode 100644 index 000000000000..7156107e3aec --- /dev/null +++ b/.next/package.json @@ -0,0 +1 @@ +{"type": "commonjs"} \ No newline at end of file diff --git a/.next/trace b/.next/trace new file mode 100644 index 000000000000..7b99b4b50402 --- /dev/null +++ b/.next/trace @@ -0,0 +1 @@ +[{"name":"generate-buildid","duration":232,"timestamp":1468878220958,"id":4,"parentId":1,"tags":{},"startTime":1775768361674,"traceId":"924db08a2af579ac"},{"name":"load-custom-routes","duration":265,"timestamp":1468878221269,"id":5,"parentId":1,"tags":{},"startTime":1775768361674,"traceId":"924db08a2af579ac"},{"name":"create-dist-dir","duration":291,"timestamp":1468878221549,"id":6,"parentId":1,"tags":{},"startTime":1775768361675,"traceId":"924db08a2af579ac"},{"name":"clean","duration":180,"timestamp":1468878222427,"id":7,"parentId":1,"tags":{},"startTime":1775768361675,"traceId":"924db08a2af579ac"},{"name":"discover-routes","duration":1720,"timestamp":1468878230093,"id":8,"parentId":1,"tags":{},"startTime":1775768361683,"traceId":"924db08a2af579ac"},{"name":"create-root-mapping","duration":28,"timestamp":1468878231910,"id":9,"parentId":1,"tags":{},"startTime":1775768361685,"traceId":"924db08a2af579ac"},{"name":"generate-route-types","duration":2297,"timestamp":1468878232337,"id":10,"parentId":1,"tags":{},"startTime":1775768361685,"traceId":"924db08a2af579ac"},{"name":"public-dir-conflict-check","duration":51,"timestamp":1468878234679,"id":11,"parentId":1,"tags":{},"startTime":1775768361688,"traceId":"924db08a2af579ac"},{"name":"generate-routes-manifest","duration":1015,"timestamp":1468878234783,"id":12,"parentId":1,"tags":{},"startTime":1775768361688,"traceId":"924db08a2af579ac"},{"name":"run-turbopack","duration":190571,"timestamp":1468878238803,"id":14,"parentId":1,"tags":{"failed":true},"startTime":1775768361692,"traceId":"924db08a2af579ac"},{"name":"next-build","duration":2998120,"timestamp":1468875431290,"id":1,"tags":{"buildMode":"default","version":"16.2.3","bundler":"turbopack","has-custom-webpack-config":"false","use-build-worker":"true","failed":true},"startTime":1775768358884,"traceId":"924db08a2af579ac"}] diff --git a/.next/trace-build b/.next/trace-build new file mode 100644 index 000000000000..2c6d0d502e13 --- /dev/null +++ b/.next/trace-build @@ -0,0 +1 @@ +[{"name":"run-turbopack","duration":190571,"timestamp":1468878238803,"id":14,"parentId":1,"tags":{"failed":true},"startTime":1775768361692,"traceId":"924db08a2af579ac"},{"name":"next-build","duration":2998120,"timestamp":1468875431290,"id":1,"tags":{"buildMode":"default","version":"16.2.3","bundler":"turbopack","has-custom-webpack-config":"false","use-build-worker":"true","failed":true},"startTime":1775768358884,"traceId":"924db08a2af579ac"}] diff --git a/snow/networking/timeout/timeoutmock/BUILD.bazel b/.next/turbopack similarity index 100% rename from snow/networking/timeout/timeoutmock/BUILD.bazel rename to .next/turbopack diff --git a/.next/types/cache-life.d.ts b/.next/types/cache-life.d.ts new file mode 100644 index 000000000000..a8c6997e7228 --- /dev/null +++ b/.next/types/cache-life.d.ts @@ -0,0 +1,145 @@ +// Type definitions for Next.js cacheLife configs + +declare module 'next/cache' { + export { unstable_cache } from 'next/dist/server/web/spec-extension/unstable-cache' + export { + updateTag, + revalidateTag, + revalidatePath, + refresh, + } from 'next/dist/server/web/spec-extension/revalidate' + export { unstable_noStore } from 'next/dist/server/web/spec-extension/unstable-no-store' + + + /** + * Cache this `"use cache"` for a timespan defined by the `"default"` profile. + * ``` + * stale: 300 seconds (5 minutes) + * revalidate: 900 seconds (15 minutes) + * expire: never + * ``` + * + * This cache may be stale on clients for 5 minutes before checking with the server. + * If the server receives a new request after 15 minutes, start revalidating new values in the background. + * It lives for the maximum age of the server cache. If this entry has no traffic for a while, it may serve an old value the next request. + */ + export function cacheLife(profile: "default"): void + + /** + * Cache this `"use cache"` for a timespan defined by the `"seconds"` profile. + * ``` + * stale: 30 seconds + * revalidate: 1 seconds + * expire: 60 seconds (1 minute) + * ``` + * + * This cache may be stale on clients for 30 seconds before checking with the server. + * If the server receives a new request after 1 seconds, start revalidating new values in the background. + * If this entry has no traffic for 1 minute it will expire. The next request will recompute it. + */ + export function cacheLife(profile: "seconds"): void + + /** + * Cache this `"use cache"` for a timespan defined by the `"minutes"` profile. + * ``` + * stale: 300 seconds (5 minutes) + * revalidate: 60 seconds (1 minute) + * expire: 3600 seconds (1 hour) + * ``` + * + * This cache may be stale on clients for 5 minutes before checking with the server. + * If the server receives a new request after 1 minute, start revalidating new values in the background. + * If this entry has no traffic for 1 hour it will expire. The next request will recompute it. + */ + export function cacheLife(profile: "minutes"): void + + /** + * Cache this `"use cache"` for a timespan defined by the `"hours"` profile. + * ``` + * stale: 300 seconds (5 minutes) + * revalidate: 3600 seconds (1 hour) + * expire: 86400 seconds (1 day) + * ``` + * + * This cache may be stale on clients for 5 minutes before checking with the server. + * If the server receives a new request after 1 hour, start revalidating new values in the background. + * If this entry has no traffic for 1 day it will expire. The next request will recompute it. + */ + export function cacheLife(profile: "hours"): void + + /** + * Cache this `"use cache"` for a timespan defined by the `"days"` profile. + * ``` + * stale: 300 seconds (5 minutes) + * revalidate: 86400 seconds (1 day) + * expire: 604800 seconds (1 week) + * ``` + * + * This cache may be stale on clients for 5 minutes before checking with the server. + * If the server receives a new request after 1 day, start revalidating new values in the background. + * If this entry has no traffic for 1 week it will expire. The next request will recompute it. + */ + export function cacheLife(profile: "days"): void + + /** + * Cache this `"use cache"` for a timespan defined by the `"weeks"` profile. + * ``` + * stale: 300 seconds (5 minutes) + * revalidate: 604800 seconds (1 week) + * expire: 2592000 seconds (1 month) + * ``` + * + * This cache may be stale on clients for 5 minutes before checking with the server. + * If the server receives a new request after 1 week, start revalidating new values in the background. + * If this entry has no traffic for 1 month it will expire. The next request will recompute it. + */ + export function cacheLife(profile: "weeks"): void + + /** + * Cache this `"use cache"` for a timespan defined by the `"max"` profile. + * ``` + * stale: 300 seconds (5 minutes) + * revalidate: 2592000 seconds (1 month) + * expire: 31536000 seconds (365 days) + * ``` + * + * This cache may be stale on clients for 5 minutes before checking with the server. + * If the server receives a new request after 1 month, start revalidating new values in the background. + * If this entry has no traffic for 365 days it will expire. The next request will recompute it. + */ + export function cacheLife(profile: "max"): void + + /** + * Cache this `"use cache"` using a custom timespan. + * ``` + * stale: ... // seconds + * revalidate: ... // seconds + * expire: ... // seconds + * ``` + * + * This is similar to Cache-Control: max-age=`stale`,s-max-age=`revalidate`,stale-while-revalidate=`expire-revalidate` + * + * If a value is left out, the lowest of other cacheLife() calls or the default, is used instead. + */ + export function cacheLife(profile: { + /** + * This cache may be stale on clients for ... seconds before checking with the server. + */ + stale?: number, + /** + * If the server receives a new request after ... seconds, start revalidating new values in the background. + */ + revalidate?: number, + /** + * If this entry has no traffic for ... seconds it will expire. The next request will recompute it. + */ + expire?: number + }): void + + + import { cacheTag } from 'next/dist/server/use-cache/cache-tag' + export { cacheTag } + + export const unstable_cacheTag: typeof cacheTag + export const unstable_cacheLife: typeof cacheLife +} diff --git a/.next/types/routes.d.ts b/.next/types/routes.d.ts new file mode 100644 index 000000000000..15617e113dac --- /dev/null +++ b/.next/types/routes.d.ts @@ -0,0 +1,55 @@ +// This file is generated automatically by Next.js +// Do not edit this file manually + +type AppRoutes = never +type PageRoutes = never +type LayoutRoutes = never +type RedirectRoutes = never +type RewriteRoutes = never +type Routes = AppRoutes | PageRoutes | LayoutRoutes | RedirectRoutes | RewriteRoutes + + +interface ParamMap { +} + + +export type ParamsOf = ParamMap[Route] + +interface LayoutSlotMap { +} + + +export type { AppRoutes, PageRoutes, LayoutRoutes, RedirectRoutes, RewriteRoutes, ParamMap } + +declare global { + /** + * Props for Next.js App Router page components + * @example + * ```tsx + * export default function Page(props: PageProps<'/blog/[slug]'>) { + * const { slug } = await props.params + * return
Blog post: {slug}
+ * } + * ``` + */ + interface PageProps { + params: Promise + searchParams: Promise> + } + + /** + * Props for Next.js App Router layout components + * @example + * ```tsx + * export default function Layout(props: LayoutProps<'/dashboard'>) { + * return
{props.children}
+ * } + * ``` + */ + type LayoutProps = { + params: Promise + children: React.ReactNode + } & { + [K in LayoutSlotMap[LayoutRoute]]: React.ReactNode + } +} diff --git a/.next/types/validator.ts b/.next/types/validator.ts new file mode 100644 index 000000000000..000dc8e06e2a --- /dev/null +++ b/.next/types/validator.ts @@ -0,0 +1,16 @@ +// This file is generated automatically by Next.js +// Do not edit this file manually +// This file validates that all pages and layouts export the correct types + + + + + + + + + + + + + diff --git a/chains/BUILD.bazel b/chains/BUILD.bazel index c3fd7f321386..834dec205fb5 100644 --- a/chains/BUILD.bazel +++ b/chains/BUILD.bazel @@ -25,6 +25,7 @@ go_library( "//network", "//network/p2p", "//proto/pb/p2p", + "//simplex", "//snow", "//snow/consensus/snowball", "//snow/consensus/snowman", @@ -68,6 +69,7 @@ go_library( "//vms/proposervm", "//vms/secp256k1fx", "//vms/tracedvm", + "@com_github_ava_labs_simplex//wal", "@com_github_prometheus_client_golang//prometheus", "@org_uber_go_zap//:zap", ], @@ -75,12 +77,34 @@ go_library( go_test( name = "chains_test", - srcs = ["subnets_test.go"], + srcs = [ + "manager_test.go", + "subnets_test.go", + ], embed = [":chains"], deps = [ + "//api/health", + "//api/metrics", + "//database/memdb", "//ids", + "//snow/consensus/simplex", + "//snow/networking/benchlist", + "//snow/networking/router", + "//snow/networking/timeout", + "//snow/networking/tracker", + "//snow/validators", "//subnets", "//utils/constants", + "//utils/crypto/bls/signer/localsigner", + "//utils/logging", + "//utils/math/meter", + "//utils/resource", + "//utils/set", + "//utils/timer", + "//vms", + "//vms/example/xsvm", + "//vms/example/xsvm/genesis", + "@com_github_prometheus_client_golang//prometheus", "@com_github_stretchr_testify//require", ], ) diff --git a/chains/manager.go b/chains/manager.go index 9ffc28d2b2ec..16f3153937c2 100644 --- a/chains/manager.go +++ b/chains/manager.go @@ -13,6 +13,7 @@ import ( "sync" "time" + "github.com/ava-labs/simplex/wal" "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" @@ -27,6 +28,7 @@ import ( "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/network" "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/simplex" "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/consensus/snowball" "github.com/ava-labs/avalanchego/snow/engine/avalanche/bootstrap/queue" @@ -88,6 +90,7 @@ const ( p2pNamespace = constants.PlatformName + metric.NamespaceSeparator + "p2p" snowmanNamespace = constants.PlatformName + metric.NamespaceSeparator + "snowman" stakeNamespace = constants.PlatformName + metric.NamespaceSeparator + "stake" + simplexWALSuffix = "_simplex.wal" ) var ( @@ -103,6 +106,9 @@ var ( // Bootstrapping prefixes for ChainVMs ChainBootstrappingDBPrefix = []byte("interval_bs") + // Prefix used for simplex storage + simplexDBPrefix = []byte("simplex") + errUnknownVMType = errors.New("the vm should have type avalanche.DAGVM or snowman.ChainVM") errCreatePlatformVM = errors.New("attempted to create a chain running the PlatformVM") errNotBootstrapped = errors.New("subnets not bootstrapped") @@ -565,11 +571,20 @@ func (m *manager) buildChain(chainParams ChainParameters, sb subnets.Subnet) (*c return nil, fmt.Errorf("error while creating new avalanche vm %w", err) } case block.ChainVM: + // handle simplex engine based off parameters + if sb.Config().SimplexParameters != nil { + chain, err = m.createSimplexChain(ctx, vm, sb, chainParams.GenesisData, chainFxs) + if err != nil { + return nil, fmt.Errorf("error while creating simplex chain %w", err) + } + break + } + beacons := m.Validators if chainParams.ID == constants.PlatformChainID { beacons = chainParams.CustomBeacons } - + m.Log.Debug("Creating snowmansjknsknsnjk!!!!!!") chain, err = m.createSnowmanChain( ctx, chainParams.GenesisData, @@ -1182,7 +1197,7 @@ func (m *manager) createSnowmanChain( if ctx.ChainID == constants.PlatformChainID { minBlockDelay = m.ProposerMinBlockDelay } - m.Log.Info("creating proposervm wrapper", + m.Log.Info("creating proposervm wrapper!!!!!", zap.Time("activationTime", m.Upgrades.ApricotPhase4Time), zap.Uint64("minPChainHeight", m.Upgrades.ApricotPhase4MinPChainHeight), zap.Duration("minBlockDelay", minBlockDelay), @@ -1615,6 +1630,238 @@ func (m *manager) getOrMakeVMGatherer(vmID ids.ID) (metrics.MultiGatherer, error return vmGatherer, nil } +// createSimplexHandler creates a handler that passes messages from the network to the consensus engine +func (m *manager) createSimplexHandler(ctx *snow.ConsensusContext, sb subnets.Subnet, primaryAlias string, connectedValidators tracker.Peers, peerTracker *p2p.PeerTracker, halter common.Halter) (handler.Handler, error) { + handlerReg, err := metrics.MakeAndRegister( + m.handlerGatherer, + primaryAlias, + ) + if err != nil { + return nil, err + } + + // Asynchronously passes messages from the network to the consensus engine + return handler.New( + ctx, + nil, // we don't need a change notifier for simplex, since the engine listens for events and we don't want the handler to intercept them + nil, + m.Validators, + m.FrontierPollFrequency, + m.ConsensusAppConcurrency, + m.ResourceTracker, + sb, + connectedValidators, + peerTracker, + handlerReg, + halter.Halt, + ) +} + +// createMessageSender creates a sender that passes messages from the consensus engine to the network +func (m *manager) createMessageSender(ctx *snow.ConsensusContext, sb subnets.Subnet) (common.Sender, error) { + msgSender, err := sender.New( + ctx, + m.MsgCreator, + m.Net, + m.ManagerConfig.Router, + m.TimeoutManager, + p2ppb.EngineType_ENGINE_TYPE_CHAIN, + sb, + ctx.Registerer, + ) + if err != nil { + return nil, fmt.Errorf("couldn't initialize sender: %w", err) + } + + if m.TracingEnabled { + msgSender = sender.Trace(msgSender, m.Tracer) + } + + return msgSender, nil +} + +func (m *manager) createTrackedPeers(primaryAlias string) (tracker.Peers, error) { + stakeReg, err := metrics.MakeAndRegister( + m.stakeGatherer, + primaryAlias, + ) + if err != nil { + return nil, err + } + connectedValidators, err := tracker.NewMeteredPeers(stakeReg) + if err != nil { + return nil, fmt.Errorf("error creating peer tracker: %w", err) + } + return connectedValidators, nil +} + +// createPeerTracker creates a peer tracker for chain engines +func (m *manager) createPeerTracker(ctx *snow.ConsensusContext, primaryAlias string) (*p2p.PeerTracker, error) { + p2pReg, err := metrics.MakeAndRegister( + m.p2pGatherer, + primaryAlias, + ) + if err != nil { + return nil, err + } + + peerTracker, err := p2p.NewPeerTracker( + ctx.Log, + "peer_tracker", + p2pReg, + set.Of(ctx.NodeID), + nil, + ) + if err != nil { + return nil, fmt.Errorf("error creating peer tracker: %w", err) + } + return peerTracker, nil +} + +func simplexCtxConfig(ctx *snow.ConsensusContext) simplex.SimplexChainContext { + return simplex.SimplexChainContext{ + NodeID: ctx.NodeID, + ChainID: ctx.ChainID, + SubnetID: ctx.SubnetID, + NetworkID: ctx.NetworkID, + } +} + +func (m *manager) createSimplexChain(ctx *snow.ConsensusContext, vm block.ChainVM, sb subnets.Subnet, genesisData []byte, fxs []*common.Fx) (*chain, error) { + ctx.Lock.Lock() + defer ctx.Lock.Unlock() + + ctx.State.Set(snow.EngineState{ + Type: p2ppb.EngineType_ENGINE_TYPE_CHAIN, + State: snow.Initializing, + }) + primaryAlias := m.PrimaryAliasOrDefault(ctx.ChainID) + m.Log.Info("creating simplex chain", zap.String("chain", primaryAlias)) + + messageSender, err := m.createMessageSender(ctx, sb) + if err != nil { + return nil, fmt.Errorf("couldn't create simplex message sender: %w", err) + } + + // initialize the VM + vmDB, simplexDB, err := m.createSimplexDBs(primaryAlias, ctx.ChainID) + if err != nil { + return nil, fmt.Errorf("couldn't create simplex DBs: %w", err) + } + + // TODO: wrap the vm in a LOCKED VM that locks the VM interface methods(excluding WaitForEvent) + + var halter common.Halter + connectedValidators, err := m.createTrackedPeers(primaryAlias) + if err != nil { + return nil, fmt.Errorf("error creating connected validators: %w", err) + } + m.Validators.RegisterSetCallbackListener(ctx.SubnetID, connectedValidators) + + peerTracker, err := m.createPeerTracker(ctx, primaryAlias) + if err != nil { + return nil, fmt.Errorf("error creating peer tracking: %w", err) + } + + h, err := m.createSimplexHandler(ctx, sb, primaryAlias, connectedValidators, peerTracker, halter) + if err != nil { + return nil, fmt.Errorf("error creating handler: %w", err) + } + + chainConfig, err := m.getChainConfig(ctx.ChainID) + if err != nil { + return nil, fmt.Errorf("error while fetching chain config: %w", err) + } + + // we need to set initialize the VM before creating the simplex engine + // in order to have the genesis data available + if err := vm.Initialize( + context.TODO(), + ctx.Context, + vmDB, + genesisData, + chainConfig.Upgrade, + chainConfig.Config, + fxs, + messageSender, + ); err != nil { + return nil, fmt.Errorf("couldn't initialize simplex VM: %w", err) + } + + walLocation := getChainWALLocation(ctx.ChainDataDir, ctx.ChainID) + chainWal := wal.New(walLocation) + config := &simplex.Config{ + Ctx: simplexCtxConfig(ctx), + Log: ctx.Log, + Sender: m.Net, + OutboundMsgBuilder: m.MsgCreator, + VM: vm, + WAL: chainWal, + SignBLS: m.ManagerConfig.StakingBLSKey.Sign, + DB: simplexDB, + Params: sb.Config().SimplexParameters, + } + + engine, err := simplex.NewEngine(context.TODO(), ctx, config) + if err != nil { + return nil, fmt.Errorf("couldn't create simplex engine: %w", err) + } + + bootstrapper := &simplex.NoopBootstrapper{ + BootstrapTracker: sb, + Engine: engine, + } + h.SetEngineManager(&handler.EngineManager{ + DAG: nil, + Chain: &handler.Engine{ + Bootstrapper: bootstrapper, + Consensus: engine, + StateSyncer: nil, // a nil StateSyncer, skips StateSync in favor of the Bootstrapper. + }, + }) + + // Register health checks + if err := m.Health.RegisterHealthCheck(primaryAlias, h, ctx.SubnetID.String()); err != nil { + return nil, fmt.Errorf("couldn't add health check for chain %s: %w", primaryAlias, err) + } + + return &chain{ + Name: primaryAlias, + Context: ctx, + VM: vm, + Handler: h, + }, nil +} + +// createSimplexDBs creates dbs for simplex. One is used for the VM to store blocks, +// the other is used for simplex to store finalizations +func (m *manager) createSimplexDBs(primaryAlias string, chainID ids.ID) (*prefixdb.Database, *prefixdb.Database, error) { + meterDBReg, err := metrics.MakeAndRegister( + m.MeterDBMetrics, + primaryAlias, + ) + if err != nil { + return nil, nil, err + } + + meterDB, err := meterdb.New(meterDBReg, m.DB) + if err != nil { + return nil, nil, err + } + + prefixDB := prefixdb.New(chainID[:], meterDB) + vmDB := prefixdb.New(VMDBPrefix, prefixDB) + simplexDB := prefixdb.New(simplexDBPrefix, prefixDB) + return vmDB, simplexDB, nil +} + +func getChainWALLocation(chainDataDir string, chainID ids.ID) string { + return filepath.Join( + chainDataDir, + chainID.String()+simplexWALSuffix, + ) +} + func getLastAcceptedHeight(vm block.ChainVM) (uint64, error) { lastAcceptedBlock, err := vm.LastAccepted(context.Background()) if err != nil { diff --git a/chains/manager_test.go b/chains/manager_test.go new file mode 100644 index 000000000000..639fe5379042 --- /dev/null +++ b/chains/manager_test.go @@ -0,0 +1,196 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package chains + +import ( + "errors" + "os" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/api/health" + "github.com/ava-labs/avalanchego/api/metrics" + "github.com/ava-labs/avalanchego/database/memdb" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/consensus/simplex" + "github.com/ava-labs/avalanchego/snow/networking/benchlist" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/snow/networking/timeout" + "github.com/ava-labs/avalanchego/snow/networking/tracker" + "github.com/ava-labs/avalanchego/snow/validators" + "github.com/ava-labs/avalanchego/subnets" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/math/meter" + "github.com/ava-labs/avalanchego/utils/resource" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/utils/timer" + "github.com/ava-labs/avalanchego/vms" + "github.com/ava-labs/avalanchego/vms/example/xsvm" + "github.com/ava-labs/avalanchego/vms/example/xsvm/genesis" +) + +// startChainCreatorNoPChain is used for testing to bypass setting up the pchain +// and unblocking the chain creator +func (m *manager) startChainCreatorNoPChain() { + m.chainCreatorExited.Add(1) + go m.dispatchChainCreator() + + // typically creating the pchain would unblock the chain creator channel + close(m.unblockChainCreatorCh) +} + +// newRouter returns a mock router that mocks the call of adding a chain to the router. +func newRouter(t *testing.T, tm *timeout.Manager) router.Router { + chainRouter := router.ChainRouter{} + require.NoError(t, chainRouter.Initialize( + ids.EmptyNodeID, + logging.NoLog{}, + tm, + time.Second, + set.Set[ids.ID]{}, + true, + set.Set[ids.ID]{}, + nil, + router.HealthConfig{}, + prometheus.NewRegistry(), + )) + + return &chainRouter +} + +func newTimeoutManager(t *testing.T) *timeout.Manager { + require := require.New(t) + + config := &timer.AdaptiveTimeoutConfig{ + InitialTimeout: 2 * time.Second, + MinimumTimeout: 1 * time.Second, + MaximumTimeout: 10 * time.Second, + TimeoutCoefficient: 1.5, + TimeoutHalflife: 10 * time.Second, + } + timeoutManager, err := timeout.NewManager( + config, + benchlist.NewNoBenchlist(), + prometheus.DefaultRegisterer, + prometheus.DefaultRegisterer, + ) + require.NoError(err) + + return timeoutManager +} + +func newTestSubnets(t *testing.T, subnetID ids.ID) *Subnets { + config := map[ids.ID]subnets.Config{ + constants.PrimaryNetworkID: {}, + subnetID: { + SimplexParameters: &simplex.Parameters{ + MaxNetworkDelay: 10 * time.Second, + MaxRebroadcastWait: 5 * time.Second, + }, + }, + } + + subnets, err := NewSubnets(ids.EmptyNodeID, config) + require.NoError(t, err) + return subnets +} + +func newTestVMManager(t *testing.T, vmID ids.ID) *vms.Manager { + vmManager := vms.NewManager(logging.NoLog{}, ids.NewAliaser()) + require.NoError(t, vmManager.RegisterFactory(t.Context(), vmID, &xsvm.Factory{})) + return vmManager +} + +func TestCreateSimplexChain(t *testing.T) { + nodeID := ids.GenerateTestNodeID() + genesisBytes, err := genesis.Codec.Marshal(genesis.CodecVersion, &genesis.Genesis{}) + require.NoError(t, err) + + chainParams := ChainParameters{ + ID: ids.GenerateTestID(), + SubnetID: ids.GenerateTestID(), + VMID: ids.GenerateTestID(), + GenesisData: genesisBytes, + } + logger := logging.NewLogger("test", logging.NewWrappedCore(logging.Debug, os.Stdout, logging.Plain.ConsoleEncoder())) + subnets := newTestSubnets(t, chainParams.SubnetID) + signer, err := localsigner.New() + require.NoError(t, err) + + resourceTracker, err := tracker.NewResourceTracker( + prometheus.DefaultRegisterer, + resource.NoUsage, + meter.ContinuousFactory{}, + time.Second, + ) + require.NoError(t, err) + + // Set the validators of the simplex chain. It must include our nodeID. + validators := validators.NewManager() + require.NoError(t, validators.AddStaker(chainParams.SubnetID, nodeID, signer.PublicKey(), ids.GenerateTestID(), 1)) + + healthChecker, err := health.New(logger, prometheus.DefaultRegisterer) + require.NoError(t, err) + + tm := newTimeoutManager(t) + router := newRouter(t, tm) + managerConfig := &ManagerConfig{ + NodeID: nodeID, + StakingBLSKey: signer, + Subnets: subnets, + + // Metrics + Metrics: metrics.NewLabelGatherer("chain"), + MeterDBMetrics: metrics.NewLabelGatherer("dbmetrics"), + + // Logging + Log: logger, + LogFactory: logging.NewFactory(logging.Config{ + LogLevel: logging.Debug, + LoggerName: "chain_logger", + }), + + VMManager: newTestVMManager(t, chainParams.VMID), + Validators: validators, + + // For handler initialization + FrontierPollFrequency: 1 * time.Second, + ConsensusAppConcurrency: 1, + ResourceTracker: resourceTracker, + + // For health check + Health: healthChecker, + + // Database + DB: memdb.New(), + + // Register the chain with router and timeout manager + TimeoutManager: tm, + Router: router, + } + + chainManager, err := New(managerConfig) + require.NoError(t, err) + chainManager.(*manager).startChainCreatorNoPChain() + + // Queue chain creation + chainManager.QueueChainCreation(chainParams) + primaryAlias := chainManager.PrimaryAliasOrDefault(chainParams.ID) + + for { + id, err := chainManager.Lookup(primaryAlias) + if errors.Is(err, ids.ErrNoIDWithAlias) { + time.Sleep(100 * time.Millisecond) + continue + } + require.NoError(t, err) + require.Equal(t, chainParams.ID, id) + return + } +} diff --git a/create_l1 b/create_l1 new file mode 100755 index 000000000000..6246dbe3bf64 Binary files /dev/null and b/create_l1 differ diff --git a/go.mod b/go.mod index 852a6d58a37f..04745920bc3e 100644 --- a/go.mod +++ b/go.mod @@ -216,6 +216,7 @@ replace github.com/ava-labs/avalanchego/graft/coreth => ./graft/coreth replace github.com/ava-labs/avalanchego/graft/subnet-evm => ./graft/subnet-evm replace github.com/ava-labs/avalanchego/graft/evm => ./graft/evm +replace github.com/ava-labs/simplex => ../simplex // Workaround for genproto ambiguous import conflict. // cockroachdb/errors@v1.9.1 -> gogo/status@v1.1.0 requires old monolithic genproto. diff --git a/graft/coreth/go.mod b/graft/coreth/go.mod index bccf6dc55a7d..8450006ee606 100644 --- a/graft/coreth/go.mod +++ b/graft/coreth/go.mod @@ -46,6 +46,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/StephenButtolph/canoto v0.18.0 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect + github.com/ava-labs/simplex v0.0.0-20260320130759-afe09323fdd1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect diff --git a/graft/coreth/go.sum b/graft/coreth/go.sum index 71ab2cc50f8d..c4891dbc0670 100644 --- a/graft/coreth/go.sum +++ b/graft/coreth/go.sum @@ -30,6 +30,8 @@ github.com/ava-labs/firewood-go-ethhash/ffi v0.3.1 h1:2LXaw0vQ/nSjSKyNil2NoNXXuH github.com/ava-labs/firewood-go-ethhash/ffi v0.3.1/go.mod h1:0Z60sYkUFvvOlOc0XHGbP+271EjP5KjaaOA0EQxCCSA= github.com/ava-labs/libevm v1.13.15-0.20260310192938-d71b6cc8513a h1:rPtNc8GdAxiCxSdL+kaM42Lfuoxi034X4Fe20lR2auI= github.com/ava-labs/libevm v1.13.15-0.20260310192938-d71b6cc8513a/go.mod h1:6NxGoR1aLABnfLy+fncXRj0W6rUoUrXghnAWZ+Rhr4o= +github.com/ava-labs/simplex v0.0.0-20260320130759-afe09323fdd1 h1:Y9UdRQj28/t+GNzVSlP6nvoNLtzNRo3fNXLUc/NscVM= +github.com/ava-labs/simplex v0.0.0-20260320130759-afe09323fdd1/go.mod h1:GVzumIo3zR23/qGRN2AdnVkIPHcKMq/D89EGWZfMGQ0= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= diff --git a/graft/subnet-evm/go.mod b/graft/subnet-evm/go.mod index 7e1f0aaa4f46..6e24313e1f0a 100644 --- a/graft/subnet-evm/go.mod +++ b/graft/subnet-evm/go.mod @@ -48,6 +48,7 @@ require ( github.com/StephenButtolph/canoto v0.18.0 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/ava-labs/avalanchego/graft/coreth v1.14.2 // indirect + github.com/ava-labs/simplex v0.0.0-20260320130759-afe09323fdd1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect diff --git a/graft/subnet-evm/go.sum b/graft/subnet-evm/go.sum index becd499d2f92..6ef1b732fb6e 100644 --- a/graft/subnet-evm/go.sum +++ b/graft/subnet-evm/go.sum @@ -32,6 +32,8 @@ github.com/ava-labs/firewood-go-ethhash/ffi v0.3.1 h1:2LXaw0vQ/nSjSKyNil2NoNXXuH github.com/ava-labs/firewood-go-ethhash/ffi v0.3.1/go.mod h1:0Z60sYkUFvvOlOc0XHGbP+271EjP5KjaaOA0EQxCCSA= github.com/ava-labs/libevm v1.13.15-0.20260310192938-d71b6cc8513a h1:rPtNc8GdAxiCxSdL+kaM42Lfuoxi034X4Fe20lR2auI= github.com/ava-labs/libevm v1.13.15-0.20260310192938-d71b6cc8513a/go.mod h1:6NxGoR1aLABnfLy+fncXRj0W6rUoUrXghnAWZ+Rhr4o= +github.com/ava-labs/simplex v0.0.0-20260320130759-afe09323fdd1 h1:Y9UdRQj28/t+GNzVSlP6nvoNLtzNRo3fNXLUc/NscVM= +github.com/ava-labs/simplex v0.0.0-20260320130759-afe09323fdd1/go.mod h1:GVzumIo3zR23/qGRN2AdnVkIPHcKMq/D89EGWZfMGQ0= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= diff --git a/plugin b/plugin new file mode 100755 index 000000000000..430b1f88ce49 Binary files /dev/null and b/plugin differ diff --git a/scripts/create-L1.sh b/scripts/create-L1.sh new file mode 100644 index 000000000000..bf3ed52bd1be --- /dev/null +++ b/scripts/create-L1.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# hex encodings of transactions. use the wallets +addValidator1="0x00000000000d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f26fc08172000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001176b038753246d52ecadcc1f77a495e33dbbb8c7d9e1ce70c4285786a5d9d7d300000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc0d7da000000010000000000000000479f66c8be895830547e70b4b298cafd433dba6e00000000680be75f00000000681e5c5f000000000000c137176b038753246d52ecadcc1f77a495e33dbbb8c7d9e1ce70c4285786a5d9d7d30000000a0000000100000000000000020000000900000001255a66ca51a499d95deb58a46fc927ce2f501df7efb05f1479b8762af8ad619d7f96efcbc4e23d182575e719622c71fde297e860d88443052205e6cb838c12d5000000000900000001255a66ca51a499d95deb58a46fc927ce2f501df7efb05f1479b8762af8ad619d7f96efcbc4e23d182575e719622c71fde297e860d88443052205e6cb838c12d5002d58bb23" +addValidator2="0x00000000000d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f26fc02b0a000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001d5e9ca417ce80de63cc07529d033783198f6fc20248e7bc5d1a780872d58bb2300000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc08172000000010000000000000000de31b4d8b22991d51aa6aa1fc733f23a851a8c9400000000680be75f00000000681e5c5f000000000000c137176b038753246d52ecadcc1f77a495e33dbbb8c7d9e1ce70c4285786a5d9d7d30000000a0000000100000000000000020000000900000001d3f1bc1c7a8c72aa7f00fe43e9dd3d1d32e8560ac507d60bad1276edc572b00a57b5fca9ac76d8a8631cd018190a3c824e7fff7f76ba99e8ef70dd8d1bb476c3000000000900000001d3f1bc1c7a8c72aa7f00fe43e9dd3d1d32e8560ac507d60bad1276edc572b00a57b5fca9ac76d8a8631cd018190a3c824e7fff7f76ba99e8ef70dd8d1bb476c30026af7599" +addValidator3="0x00000000000d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f26fbfd4a2000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001cbcd1e65c18af863583f16f17fe7a2c6f78182f7fef227495bbb13bd26af759900000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc02b0a000000010000000000000000e9094f73698002fd52c90819b457b9fbc866ab8000000000680be75f00000000681e5c5f000000000000c137176b038753246d52ecadcc1f77a495e33dbbb8c7d9e1ce70c4285786a5d9d7d30000000a000000010000000000000002000000090000000144399f600ca598c171e5e61850b84d0abee869d6cffaa1c448d34257c4f6fd41452112a75ffcbbad87b646aa78c989747356ee5c94ac578d56a4dbbf2f36ba3201000000090000000144399f600ca598c171e5e61850b84d0abee869d6cffaa1c448d34257c4f6fd41452112a75ffcbbad87b646aa78c989747356ee5c94ac578d56a4dbbf2f36ba320131fd5bcf" +addValidator4="0x00000000000d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700470de4df81a998000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001000000000000000000000000000000000000000000000000000000000000000000000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500470de4df820000000000010000000000000000aa18d3991cf637aa6c162f5e95cf163f69cd829100000000680be75f00000000681e5c5f000000000000c137176b038753246d52ecadcc1f77a495e33dbbb8c7d9e1ce70c4285786a5d9d7d30000000a000000010000000000000002000000090000000189f6ef5bba8726ed5c2050dd218f4184a2f250741b2829def3cd7b2db3410c1e223fdbe8ad51e78b75601e506877a2f8be593aa1f760f06ab99baa7018b4833d01000000090000000189f6ef5bba8726ed5c2050dd218f4184a2f250741b2829def3cd7b2db3410c1e223fdbe8ad51e78b75601e506877a2f8be593aa1f760f06ab99baa7018b4833d0111d5f544" +addValidator5="0x00000000000d00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700470de4df815330000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000019933739f11b40aecaf496f862d0bec6860d7a6e3060c0e6a9f90d94711d5f54400000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500470de4df81a998000000010000000000000000f29bce5f34a74301eb0de716d5194e4a4aea5d7a00000000680be75f00000000681e5c5f000000000000c137176b038753246d52ecadcc1f77a495e33dbbb8c7d9e1ce70c4285786a5d9d7d30000000a000000010000000000000002000000090000000154be2e8d6b5d89403b015215fe5c61a42afa03d33d91cadfb7466c929681d5d5752c00aeb664f50afc529201aa3304e9ab7c6c77a83f79ca903ec101d1f1b03e01000000090000000154be2e8d6b5d89403b015215fe5c61a42afa03d33d91cadfb7466c929681d5d5752c00aeb664f50afc529201aa3304e9ab7c6c77a83f79ca903ec101d1f1b03e013d739cd2" + +createSubnet="0x00000000001000003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000007002386f26fc0d7da000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c00000001000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db00000005002386f26fc100000000000100000000000000000000000b000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000010000000900000001c10487da88ee59569fefcb95b2c9ed8a66b033fad707a6619cb4ddad21ddeb7e018bf56e2b983a94ede093f22666493d256cd6f9a2aab7ff79233f492a54d86a00a5d9d7d3" + +createChain="0x00000000000f00003039000000000000000000000000000000000000000000000000000000000000000000000001dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000700470de4df80250c000000000000000000000001000000013cb7d3842e8cee6a0ebd09f1fe884f6861e1b29c000000011cdb4f55926af9a39bf7b459ea86208d0c9820adb63c7918cccde47e3d739cd200000000dbcf890f77f49b96857648b72b77f9f82937f28a68704af05da0dc12ba53f2db0000000500470de4df815330000000010000000000000000176b038753246d52ecadcc1f77a495e33dbbb8c7d9e1ce70c4285786a5d9d7d3000973696d706c6578766d7375626e657465766d000000000000000000000000000000000000000000000000000000000073a77b0a2020202022636f6e666967223a207b0a2020202020202020226265726c696e426c6f636b223a20302c0a20202020202020202262797a616e7469756d426c6f636b223a20302c0a202020202020202022636861696e4964223a20353631312c0a202020202020202022636f6e7374616e74696e6f706c65426c6f636b223a20302c0a202020202020202022636f6e74726163744e61746976654d696e746572436f6e666967223a207b0a20202020202020202020202022656e61626c6564416464726573736573223a205b0a2020202020202020202020202020202022307830666565646330646530303030303030303030303030303030303030303030303030303030303030220a2020202020202020202020205d2c0a20202020202020202020202022626c6f636b54696d657374616d70223a20313734353631343033370a20202020202020207d2c0a202020202020202022656970313530426c6f636b223a20302c0a202020202020202022656970313535426c6f636b223a20302c0a202020202020202022656970313538426c6f636b223a20302c0a202020202020202022666565436f6e666967223a207b0a202020202020202020202020226761734c696d6974223a2031323030303030302c0a20202020202020202020202022746172676574426c6f636b52617465223a20322c0a202020202020202020202020226d696e42617365466565223a2032353030303030303030302c0a20202020202020202020202022746172676574476173223a2036303030303030302c0a20202020202020202020202022626173654665654368616e676544656e6f6d696e61746f72223a2033362c0a202020202020202020202020226d696e426c6f636b476173436f7374223a20302c0a202020202020202020202020226d6178426c6f636b476173436f7374223a20313030303030302c0a20202020202020202020202022626c6f636b476173436f737453746570223a203230303030300a20202020202020207d2c0a202020202020202022686f6d657374656164426c6f636b223a20302c0a202020202020202022697374616e62756c426c6f636b223a20302c0a2020202020202020226c6f6e646f6e426c6f636b223a20302c0a2020202020202020226d756972476c6163696572426c6f636b223a20302c0a20202020202020202270657465727362757267426c6f636b223a20302c0a2020202020202020227265776172644d616e61676572436f6e666967223a207b0a20202020202020202020202022626c6f636b54696d657374616d70223a20313734353631343033370a20202020202020207d2c0a20202020202020202277617270436f6e666967223a207b0a20202020202020202020202022626c6f636b54696d657374616d70223a20313734353631343033372c0a2020202020202020202020202271756f72756d4e756d657261746f72223a2036372c0a20202020202020202020202022726571756972655072696d6172794e6574776f726b5369676e657273223a20747275650a20202020202020207d0a202020207d2c0a20202020226e6f6e6365223a2022307830222c0a202020202274696d657374616d70223a202230783638306266346435222c0a2020202022657874726144617461223a20223078222c0a20202020226761734c696d6974223a20223078623731623030222c0a2020202022646966666963756c7479223a2022307830222c0a20202020226d697848617368223a2022307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c0a2020202022636f696e62617365223a2022307830303030303030303030303030303030303030303030303030303030303030303030303030303030222c0a2020202022616c6c6f63223a207b0a20202020202020202230646561646330646530303030303030303030303030303030303030303030303030303030303030223a207b0a20202020202020202020202022636f6465223a202230783630383036303430353233343830313536313030306635373566383066643562353036303034333631303631303035353537356633353630653031633830363334663232343239663134363130303539353738303633356463633933393131343631303037663537383036336139373738613761313436313030386135373830363361666261383738613134363130306136353738303633626236356232343231343631303063303537356235663830666435623631303036633631303036373336363030343631303164623536356236313031303035363562363034303531393038313532363032303031356236303430353138303931303339306633356236313030366336333031653133333830383135363562363130303933363132373130383135363562363034303531363166666666393039313136383135323630323030313631303037363536356236313030616536303530383135363562363034303531363066663930393131363831353236303230303136313030373635363562363130306537376630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030383135363562363034303531363766666666666666666666666666666666393039313136383135323630323030313631303037363536356235663630353036313031306438363835363130323439353635623631303131373931393036313032373135363562363766666666666666666666666666666666313636313031326338333630363436313032373135363562363766666666666666666666666666666666313631303135363130313433353735303566363130316236353635623631323731303633303165313333383036313031353538363836363130323439353635623637666666666666666666666666666666663136376630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030363766666666666666666666666666666666313638393631303139353931393036313032396435363562363130313966393139303631303239643536356236313031613939313930363130326261353635623631303162333931393036313032626135363562393035303562393539343530353035303530353035363562383033353637666666666666666666666666666666663831313638313134363130316436353735663830666435623931393035303536356235663830356638303566363061303836383830333132313536313031656635373566383066643562383533353934353036313031666636303230383730313631303162663536356239333530363130323064363034303837303136313031626635363562393235303631303231623630363038373031363130316266353635623931353036313032323936303830383730313631303162663536356239303530393239353530393239353930393335303536356236333465343837623731363065303162356635323630313136303034353236303234356666643562363766666666666666666666666666666666383238313136383238323136303339303830383231313135363130323661353736313032366136313032333535363562353039323931353035303536356236376666666666666666666666666666666638313831313638333832313630323830383231363931393038323831313436313032393535373631303239353631303233353536356235303530393239313530353035363562383038323032383131353832383230343834313431373631303262343537363130326234363130323335353635623932393135303530353635623566383236313032643435373633346534383762373136306530316235663532363031323630303435323630323435666664356235303034393035366665613136343733366636633633343330303038313930303061222c0a2020202020202020202020202273746f72616765223a207b0a2020202020202020202020202020202022307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030223a2022307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303634220a2020202020202020202020207d2c0a2020202020202020202020202262616c616e6365223a2022307830222c0a202020202020202020202020226e6f6e6365223a2022307831220a20202020202020207d2c0a20202020202020202230666565646330646530303030303030303030303030303030303030303030303030303030303030223a207b0a20202020202020202020202022636f6465223a202230783630383036303430353233363631303031333537363130303131363130303137353635623030356236313030313135623631303031663631303136393536356236303031363030313630613031623033313633333033363130313566353736303630363030313630303136306530316230333139363030303335313636333634643331383064363065313162383130313631303035613537363130303533363130313963353635623931353036313031353735363562363335383730383662643630653131623630303136303031363065303162303331393832313630313631303037613537363130303533363130316633353635623633303730643763363936306534316236303031363030313630653031623033313938323136303136313030396135373631303035333631303233393536356236323165623936663630653631623630303136303031363065303162303331393832313630313631303062393537363130303533363130323661353635623633613339663235653536306530316236303031363030313630653031623033313938323136303136313030643935373631303035333631303261613536356236303430353136323436316263643630653531623831353236303230363030343832303135323630343236303234383230313532376635343732363136653733373036313732363536653734353537303637373236313634363536313632366336353530373236663738373933613230363136343664363034343832303135323766363936653230363336313665366536663734323036363631366336633632363136333662323037343666323037303732366637383739323037343631373236373630363438323031353236313139356436306632316236303834383230313532363061343031356236303430353138303931303339306664356238313531363032303833303166333562363130313637363130326265353635623536356236303030376662353331323736383461353638623331373361653133623966386136303136653234336536336236653865653131373864366137313738353062356436313033356235343630303136303031363061303162303331363931393035303536356236303630363130316136363130326365353635623630303036313031623533363630303438313834363130363833353635623831303139303631303163323931393036313036633935363562393035303631303164663831363034303531383036303230303136303430353238303630303038313532353036303030363130326439353635623530353036303430383035313630323038313031393039313532363030303831353239303536356236303630363030303830363130323035333636303034383138343631303638333536356238313031393036313032313239313930363130366661353635623931353039313530363130323232383238323630303136313032643935363562363034303531383036303230303136303430353238303630303038313532353039323530353035303930353635623630363036313032343336313032636535363562363030303631303235323336363030343831383436313036383335363562383130313930363130323566393139303631303663393536356239303530363130316466383136313033303535363562363036303631303237343631303263653536356236303030363130323765363130313639353635623630343038303531363030313630303136306130316230333833313636303230383230313532393139323530303136303430353136303230383138333033303338313532393036303430353239313530353039303536356236303630363130326234363130326365353635623630303036313032376536313033356335363562363130313637363130326339363130333563353635623631303336623536356233343135363130313637353736303030383066643562363130326532383336313033386635363562363030303832353131313830363130326566353735303830356231353631303330303537363130326665383338333631303363663536356235303562353035303530353635623766376536343464373934323266313763303165343839346235663466353838643333316562666132383635336434326165383332646335396533386339373938663631303332653631303136393536356236303430383035313630303136303031363061303162303339323833313638313532393138343136363032303833303135323031363034303531383039313033393061313631303335393831363130336662353635623530353635623630303036313033363636313034613435363562393035303930353635623336363030303830333736303030383033363630303038343561663433643630303038303365383038303135363130333861353733643630303066333562336436303030666435623631303339383831363130346363353635623630343035313630303136303031363061303162303338323136393037666263376364373561323065653237666439616465626162333230343166373535323134646263366266666139306363303232356233396461326535633264336239303630303039306132353035363562363036303631303366343833383336303430353138303630363030313630343035323830363032373831353236303230303136313038333036303237393133393631303536303536356239333932353035303530353635623630303136303031363061303162303338313136363130343630353736303430353136323436316263643630653531623831353236303230363030343832303135323630323636303234383230313532376634353532343333313339333633373361323036653635373732303631363436643639366532303639373332303734363836353230376136353732366632303631363034343832303135323635363436343732363537333733363064303162363036343832303135323630383430313631303134653536356238303766623533313237363834613536386233313733616531336239663861363031366532343365363362366538656531313738643661373137383530623564363130333562383035343630303136303031363061303162303331393136363030313630303136306130316230333932393039323136393139303931313739303535353035363562363030303766333630383934613133626131613332313036363763383238343932646239386463613365323037366363333733356139323061336361353035643338326262633631303138643536356236303031363030313630613031623033383131363362363130353339353736303430353136323436316263643630653531623831353236303230363030343832303135323630326436303234383230313532376634353532343333313339333633373361323036653635373732303639366437303663363536643635366537343631373436393666366532303639373332303665363034343832303135323663316264643038313834383138646264623964316339383538646436303961316236303634383230313532363038343031363130313465353635623830376633363038393461313362613161333231303636376338323834393264623938646361336532303736636333373335613932306133636135303564333832626263363130343833353635623630363036303030383038353630303136303031363061303162303331363835363034303531363130353764393139303631303765303536356236303030363034303531383038333033383138353561663439313530353033643830363030303831313436313035623835373630343035313931353036303166313936303366336430313136383230313630343035323364383235323364363030303630323038343031336536313035626435363562363036303931353035623530393135303931353036313035636538363833383338373631303564383536356239363935353035303530353035303530353635623630363038333135363130363437353738323531363030303033363130363430353736303031363030313630613031623033383531363362363130363430353736303430353136323436316263643630653531623831353236303230363030343832303135323630316436303234383230313532376634313634363437323635373337333361323036333631366336633230373436663230366536663665326436333666366537343732363136333734303030303030363034343832303135323630363430313631303134653536356235303831363130363531353635623631303635313833383336313036353935363562393439333530353035303530353635623831353131353631303636393537383135313830383336303230303166643562383036303430353136323436316263643630653531623831353236303034303136313031346539313930363130376663353635623630303038303835383531313135363130363933353736303030383066643562383338363131313536313036613035373630303038306664356235303530383230313933393139303932303339313530353635623830333536303031363030313630613031623033383131363831313436313036633435373630303038306664356239313930353035363562363030303630323038323834303331323135363130366462353736303030383066643562363130336634383236313036616435363562363334653438376237313630653031623630303035323630343136303034353236303234363030306664356236303030383036303430383338353033313231353631303730643537363030303830666435623631303731363833363130366164353635623931353036303230383330313335363766666666666666666666666666666666383038323131313536313037333335373630303038306664356238313835303139313530383536303166383330313132363130373437353736303030383066643562383133353831383131313135363130373539353736313037353936313036653435363562363034303531363031663832303136303166313939303831313636303366303131363831303139303833383231313831383331303137313536313037383135373631303738313631303665343536356238313630343035323832383135323838363032303834383730313031313131353631303739613537363030303830666435623832363032303836303136303230383330313337363030303630323038343833303130313532383039353530353035303530353035303932353039323930353035363562363030303562383338313130313536313037643735373831383130313531383338323031353236303230303136313037626635363562353035303630303039313031353235363562363030303832353136313037663238313834363032303837303136313037626335363562393139303931303139323931353035303536356236303230383135323630303038323531383036303230383430313532363130383162383136303430383530313630323038373031363130376263353635623630316630313630316631393136393139303931303136303430303139323931353035303536666534313634363437323635373337333361323036633666373732643663363537363635366332303634363536633635363736313734363532303633363136633663323036363631363936633635363461323634363937303636373335383232313232306232323938346562316633333438663562323134383836326236663830333932653439376533633635643064326366626235653533643733376535613663366136343733366636633633343330303038313930303333222c0a2020202020202020202020202273746f72616765223a207b0a2020202020202020202020202020202022307833363038393461313362613161333231303636376338323834393264623938646361336532303736636333373335613932306133636135303564333832626263223a2022307830303030303030303030303030303030303030303030303030633064656261356530303030303030303030303030303030303030303030303030303030303030222c0a2020202020202020202020202020202022307862353331323736383461353638623331373361653133623966386136303136653234336536336236653865653131373864366137313738353062356436313033223a2022307830303030303030303030303030303030303030303030303063306666656531323334353637383930616263646566313233343536373839306162636465663334220a2020202020202020202020207d2c0a2020202020202020202020202262616c616e6365223a2022307830222c0a202020202020202020202020226e6f6e6365223a2022307831220a20202020202020207d2c0a20202020202020202231333738663639343731646331336262363562396661323561393661396432366135333064326565223a207b0a2020202020202020202020202262616c616e6365223a20223078323038366163333531303532363030303030220a20202020202020207d2c0a20202020202020202238646239376337636563653234396332623938626463303232366363346332613537626635326663223a207b0a2020202020202020202020202262616c616e6365223a202230786433633231626365636365646131303030303030220a20202020202020207d2c0a20202020202020202239633030363239636537313262303235356231376134613635373137316163643135373230623863223a207b0a20202020202020202020202022636f6465223a202230783733303030303030303030303030303030303030303030303030303030303030303030303030303030303330313436303830363034303532363030343336313036313030623135373566333536306530316338303633383534613839336631313631303037393537383036333835346138393366313436313031623235373830363338373431386238653134363130323066353738303633396238333534363531343631303232323537383036336136393963313335313436313032343235373830363365316436386633303134363130323535353738303633656239376365353131343631303236383537356638306664356238303633303231646538386631343631303062353537383036333038386332343633313436313030653235373830363334643834373838343134363130313132353738303633353037383262306631343631303133333537383036333766376334323761313436313031366235373562356638306664356236313030633836313030633333363630303436313138613935363562363130323839353635623630343038303531393238333532393031353135363032303833303135323031356236303430353138303931303339306633356236313030663536313030663033363630303436313138613935363562363130343461353635623630343038303531393238333532363030313630303136303430316230333930393131363630323038333031353230313631303064393536356236313031323536313031323033363630303436313138613935363562363130363362353635623630343035313930383135323630323030313631303064393536356236313031343636313031343133363630303436313138613935363562363130376338353635623630343038303531393338343532363030313630303136303430316230333932383331363630323038353031353239313136393038323031353236303630303136313030643935363562363130316135363130313739333636303034363131386532353635623630343038303531356636303230383230313831393035323630323238323031353236303236383038323031393339303933353238313531383038323033393039333031383335323630343630313930353239303536356236303430353136313030643939313930363131393436353635623631303161353631303163303336363030343631313937613536356236303430383035313566363032303832303135323630303336306530316236303232383230313532363032363831303139343930393435323630303136303031363063303162303331393630633039333834316238313136363034363836303135323931393039323162313636303465383330313532383035313830383330333630333630313831353236303536393039323031393035323930353635623631303161353631303231643336363030343631313965623536356236313061316535363562363130323335363130323330333636303034363131386139353635623631306236303536356236303430353136313030643939313930363131626234353635623631303161353631303235303336363030343631316336623536356236313134616235363562363130316135363130323633333636303034363131633964353635623631313465663536356236313032376236313032373633363630303436313164383035363562363131353235353635623630343035313631303064393932393139303631316537633536356235663830383235313630323731343631303263343537383235313630343035313633636339326461613136306530316238313532363366666666666666663930393131363630303438323031353236303237363032343832303135323630343430313562363034303531383039313033393066643562356638303562363030323831313031353631303331333537363130326462383136303031363131656138353635623631303265363930363030383631316562623536356236316666666631363835383238313531383131303631303266633537363130326663363131656432353635623031363032303031353136306638316339303162393139303931313739303630303130313631303263373536356235303631666666663831313631353631303333643537363034303531363334303762353837333630653031623831353236316666666638323136363030343832303135323630323430313631303262623536356235663830356236303034383131303135363130333938353736313033353438313630303336313165613835363562363130333566393036303038363131656262353635623633666666666666666631363836363130333731383336303032363131656536353635623831353138313130363130333831353736313033383136313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130333430353635623530363366666666666666663831313636303032313436313033633035373630343035313633356236303839326636306530316238313532363030343031363034303531383039313033393066643562356638303562363032303831313031353631303431353537363130336437383136303166363131656138353635623631303365323930363030383631316562623536356238373631303365653833363030363631316565363536356238313531383131303631303366653537363130336665363131656432353635623031363032303031353136306638316339303162393139303931313739303630303130313631303363333536356235303566383636303236383135313831313036313034326135373631303432613631316564323536356230313630323030313531393139373630303136303031363066383162303331393930393231363135313539363530393039343530353035303530353035363562356638303832353136303265313436313034383035373832353136303430353136336363393264616131363065303162383135323633666666666666666639303931313636303034383230313532363032653630323438323031353236303434303136313032626235363562356638303562363030323831313031353631303463663537363130343937383136303031363131656138353635623631303461323930363030383631316562623536356236316666666631363835383238313531383131303631303462383537363130346238363131656432353635623031363032303031353136306638316339303162393139303931313739303630303130313631303438333536356235303631666666663831313631353631303466393537363034303531363334303762353837333630653031623831353236316666666638323136363030343832303135323630323430313631303262623536356235663830356236303034383131303135363130353534353736313035313038313630303336313165613835363562363130353162393036303038363131656262353635623633666666666666666631363836363130353264383336303032363131656536353635623831353138313130363130353364353736313035336436313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130346663353635623530363366666666666666663831313631353631303537613537363034303531363335623630383932663630653031623831353236303034303136303430353138303931303339306664356235663830356236303230383131303135363130356366353736313035393138313630316636313165613835363562363130353963393036303038363131656262353635623837363130356138383336303036363131656536353635623831353138313130363130356238353736313035623836313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130353764353635623530356638303562363030383831313031353631303632653537363130356537383136303037363131656138353635623631303566323930363030383631316562623536356236303031363030313630343031623033313638383631303630373833363032363631316565363536356238313531383131303631303631373537363130363137363131656432353635623031363032303031353136306638316339303162393139303931313739303630303130313631303564333536356235303930393639303935353039333530353035303530353635623566383135313630323631343631303637303537383135313630343035313633636339326461613136306530316238313532363366666666666666663930393131363630303438323031353236303236363032343832303135323630343430313631303262623536356235663830356236303032383131303135363130366266353736313036383738313630303136313165613835363562363130363932393036303038363131656262353635623631666666663136383438323831353138313130363130366138353736313036613836313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130363733353635623530363166666666383131363135363130366539353736303430353136333430376235383733363065303162383135323631666666663832313636303034383230313532363032343031363130326262353635623566383035623630303438313130313536313037343435373631303730303831363030333631316561383536356236313037306239303630303836313165626235363562363366666666666666663136383536313037316438333630303236313165653635363562383135313831313036313037326435373631303732643631316564323536356230313630323030313531363066383163393031623931393039313137393036303031303136313036656335363562353036336666666666666666383131363135363130373661353736303430353136333562363038393266363065303162383135323630303430313630343035313830393130333930666435623566383035623630323038313130313536313037626635373631303738313831363031663631316561383536356236313037386339303630303836313165626235363562383636313037393838333630303636313165653635363562383135313831313036313037613835373631303761383631316564323536356230313630323030313531363066383163393031623931393039313137393036303031303136313037366435363562353039343933353035303530353035363562356638303566383335313630333631343631303766663537383335313630343035313633636339326461613136306530316238313532363366666666666666663930393131363630303438323031353236303336363032343832303135323630343430313631303262623536356235663830356236303032383131303135363130383465353736313038313638313630303136313165613835363562363130383231393036303038363131656262353635623631666666663136383638323831353138313130363130383337353736313038333736313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130383032353635623530363166666666383131363135363130383738353736303430353136333430376235383733363065303162383135323631666666663832313636303034383230313532363032343031363130326262353635623566383035623630303438313130313536313038643335373631303838663831363030333631316561383536356236313038396139303630303836313165626235363562363366666666666666663136383736313038616338333630303236313165653635363562383135313831313036313038626335373631303862633631316564323536356230313630323030313531363066383163393031623931393039313137393036303031303136313038376235363562353036336666666666666666383131363630303331343631303866623537363034303531363335623630383932663630653031623831353236303034303136303430353138303931303339306664356235663830356236303230383131303135363130393530353736313039313238313630316636313165613835363562363130393164393036303038363131656262353635623838363130393239383336303036363131656536353635623831353138313130363130393339353736313039333936313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130386665353635623530356638303562363030383831313031353631303961663537363130393638383136303037363131656138353635623631303937333930363030383631316562623536356236303031363030313630343031623033313638393631303938383833363032363631316565363536356238313531383131303631303939383537363130393938363131656432353635623031363032303031353136306638316339303162393139303931313739303630303130313631303935343536356235303566383035623630303838313130313536313061306535373631303963373831363030373631316561383536356236313039643239303630303836313165626235363562363030313630303136303430316230333136386136313039653738333630326536313165653635363562383135313831313036313039663735373631303966373631316564323536356230313630323030313531363066383163393031623931393039313137393036303031303136313039623335363562353039313938393039373530393039353530393335303530353035303536356238303531363032303830383330313531363034303830383530313531363036303836383130313531353139323531356639353831303138363930353236303232383130313936393039363532363034323836303139333930393335323630303536306532316236303632383630313532366266666666666666666666666666666666666666666666666631393930383331623136363036363835303135323630653031623630303136303031363065303162303331393136363037613834303135323931363037653031363034303531363032303831383330333033383135323930363034303532393035303566356238333630363030313531353138313130313536313062353935373831383436303630303135313832383135313831313036313061626135373631306162613631316564323536356236303230303236303230303130313531356630313531353138353630363030313531383338313531383131303631306164633537363130616463363131656432353635623630323030323630323030313031353135663031353138363630363030313531383438313531383131303631306166643537363130616664363131656432353635623630323030323630323030313031353136303230303135313837363036303031353138353831353138313130363130623166353736313062316636313165643235363562363032303032363032303031303135313630343030313531363034303531363032303031363130623366393539343933393239313930363131656639353635623630343038303531363031663139383138343033303138313532393139303532393135303630303130313631306139363536356235303932393135303530353635623631306236383631313731323536356235663631306237313631313731323536356235663830356236303032383131303135363130626366353736313062383838313630303136313165613835363562363130623933393036303038363131656262353635623631666666663136383636313062613836336666666666666666383731363834363131656536353635623831353138313130363130626238353736313062623836313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130623734353635623530363166666666383131363135363130626639353736303430353136333430376235383733363065303162383135323631666666663832313636303034383230313532363032343031363130326262353635623631306330343630303238343631316637323536356239323530353035663830356236303034383131303135363130633639353736313063316538313630303336313165613835363562363130633239393036303038363131656262353635623633666666666666666631363836383536336666666666666666313638333631306334323931393036313165653635363562383135313831313036313063353235373631306335323631316564323536356230313630323030313531363066383163393031623931393039313137393036303031303136313063306135363562353036336666666666666666383131363630303131343631306339313537363034303531363335623630383932663630653031623831353236303034303136303430353138303931303339306664356236313063396336303034383436313166373235363562393235303530356638303562363032303831313031353631306366393537363130636236383136303166363131656138353635623631306363313930363030383631316562623536356238363631306364323633666666666666666638373136383436313165653635363562383135313831313036313063653235373631306365323631316564323536356230313630323030313531363066383163393031623931393039313137393036303031303136313063613235363562353038303832353236313064303836303230383436313166373235363562393235303530356638303562363030343831313031353631306436643537363130643232383136303033363131656138353635623631306432643930363030383631316562623536356236336666666666666666313638363835363366666666666666663136383336313064343639313930363131656536353635623831353138313130363130643536353736313064353636313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130643065353635623530363130643739363030343834363131663732353635623932353035663831363366666666666666663136363030313630303136303430316230333831313131353631306439613537363130643961363131373663353635623630343035313930383038323532383036303166303136303166313931363630323030313832303136303430353238303135363130646334353736303230383230313831383033363833333730313930353035623530393035303566356238323633666666666666666631363831313031353631306533333537383636313064653836336666666666666666383731363833363131656536353635623831353138313130363130646638353736313064663836313165643235363562363032303031303135313630663831633630663831623832383238313531383131303631306531353537363130653135363131656432353635623630323030313031393036303031363030313630663831623033313931363930383135663161393035333530363030313031363130646339353635623530363032303833303138313930353236313065343538323835363131663732353635623630343038303531363033303830383235323630363038323031393039323532393139353530356639323530363032303832303138313830333638333337303139303530353039303530356635623630333038313130313536313065643135373836363130653836363366666666666666663837313638333631316565363536356238313531383131303631306539363537363130653936363131656432353635623630323030313031353136306638316336306638316238323832383135313831313036313065623335373631306562333631316564323536356236303230303130313930363030313630303136306638316230333139313639303831356631613930353335303630303130313631306536633536356235303630343038333031383139303532363130656534363033303835363131663732353635623933353035303566383035623630303838313130313536313066346135373631306566653831363030373631316561383536356236313066303939303630303836313165626235363562363030313630303136303430316230333136383736313066323336336666666666666666383831363834363131656536353635623831353138313130363130663333353736313066333336313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130656561353635623530363030313630303136303430316230333831313636303630383430313532363130663635363030383835363131663732353635623933353035303566383035663562363030343831313031353631306663623537363130663830383136303033363131656138353635623631306638623930363030383631316562623536356236336666666666666666313638383837363366666666666666663136383336313066613439313930363131656536353635623831353138313130363130666234353736313066623436313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363130663663353635623530363130666437363030343836363131663732353635623934353035663562363030343831313031353631313033613537363130666566383136303033363131656138353635623631306666613930363030383631316562623536356236336666666666666666313638383837363366666666666666663136383336313130313339313930363131656536353635623831353138313130363131303233353736313130323336313165643235363562303136303230303135313630663831633930316239323930393231373931363030313031363130666462353635623530363131303436363030343836363131663732353635623934353035663832363366666666666666663136363030313630303136303430316230333831313131353631313036373537363131303637363131373663353635623630343035313930383038323532383036303230303236303230303138323031363034303532383031353631313039303537383136303230303136303230383230323830333638333337303139303530356235303930353035663562383336336666666666666666313638313130313536313131373835373630343038303531363031343830383235323831383330313930393235323566393136303230383230313831383033363833333730313930353035303930353035663562363031343831313031353631313132613537386136313130646636336666666666666666386231363833363131656536353635623831353138313130363131306566353736313130656636313165643235363562363032303031303135313630663831633630663831623832383238313531383131303631313130633537363131313063363131656432353635623630323030313031393036303031363030313630663831623033313931363930383135663161393035333530363030313031363131306335353635623530356636303134383230313531393035303830383438343831353138313130363131313436353736313131343636313165643235363562363030313630303136306130316230333930393231363630323039323833303239313930393130313930393130313532363131313639363031343861363131663732353635623938353035303530383036303031303139303530363131303935353635623530363034303830353138303832303139303931353236336666666666666666393039323136383235323630323038323031353236303830383430313532356638303830356236303034383131303135363131316661353736313131616638313630303336313165613835363562363131316261393036303038363131656262353635623633666666666666666631363839383836336666666666666666313638333631313164333931393036313165653635363562383135313831313036313131653335373631313165333631316564323536356230313630323030313531363066383163393031623931393039313137393036303031303136313131396235363562353036313132303636303034383736313166373235363562393535303566356236303034383131303135363131323639353736313132316538313630303336313165613835363562363131323239393036303038363131656262353635623633666666666666666631363839383836336666666666666666313638333631313234323931393036313165653635363562383135313831313036313132353235373631313235323631316564323536356230313630323030313531363066383163393031623932393039323137393136303031303136313132306135363562353036313132373536303034383736313166373235363562393535303566383236336666666666666666313636303031363030313630343031623033383131313135363131323936353736313132393636313137366335363562363034303531393038303832353238303630323030323630323030313832303136303430353238303135363131326266353738313630323030313630323038323032383033363833333730313930353035623530393035303566356238333633666666666666666631363831313031353631313361373537363034303830353136303134383038323532383138333031393039323532356639313630323038323031383138303336383333373031393035303530393035303566356236303134383131303135363131333539353738623631313330653633666666666666666638633136383336313165653635363562383135313831313036313133316535373631313331653631316564323536356236303230303130313531363066383163363066383162383238323831353138313130363131333362353736313133336236313165643235363562363032303031303139303630303136303031363066383162303331393136393038313566316139303533353036303031303136313132663435363562353035663630313438323031353139303530383038343834383135313831313036313133373535373631313337353631316564323536356236303031363030313630613031623033393039323136363032303932383330323931393039313031393039313031353236313133393836303134386236313166373235363562393935303530353038303630303130313930353036313132633435363562353036303430383035313830383230313930393135323633666666666666666639303932313638323532363032303832303135323630613038353031353235663631313364313832383436313166373235363562363131336463393036303134363131663866353635623631313365373835363037613631316637323536356236313133663139313930363131663732353635623930353038303633666666666666666631363838353131343631313432643537383735313630343035313633636339326461613136306530316238313532363366666666666666663931383231363630303438323031353239303832313636303234383230313532363034343031363130326262353635623566383035623630303838313130313536313134393035373631313434343831363030373631316561383536356236313134346639303630303836313165626235363562363030313630303136303430316230333136386136313134363936336666666666666666386231363834363131656536353635623831353138313130363131343739353736313134373936313165643235363562303136303230303135313630663831633930316239313930393131373930363030313031363131343330353635623530363030313630303136303430316230333136363063303836303135323530393239363935353035303530353035303530353635623630343035313566363032303832303135323630303136306531316236303232383230313532363032363831303138333930353238313135313536306638316236303436383230313532363036303930363034373031356236303430353136303230383138333033303338313532393036303430353239303530356239323931353035303536356236303430353135663630323038323031383139303532363032323832303135323630323638313031383339303532363030313630303136306330316230333139363063303833393031623136363034363832303135323630363039303630346530313631313464373536356235663630363038323630343030313531353136303330313436313135346535373630343035313633313830666661306436306530316238313532363030343031363034303531383039313033393066643562383235313630323038303835303135313830353136303430383038383031353136303630383930313531363038303861303135313830353139303837303135313531393335313566393836313135386639383861393836303031393839323937393239363930393539303934393039333930393239313031363131666237353635623630343035313630323038313833303330333831353239303630343035323930353035663562383436303830303135313630323030313531353138313130313536313136303135373831383536303830303135313630323030313531383238313531383131303631313563653537363131356365363131656432353635623630323030323630323030313031353136303430353136303230303136313135653739323931393036313230373135363562363034303830353136303166313938313834303330313831353239313930353239313530363030313031363131356132353635623530363061303834303135313830353136303230393138323031353135313630343035313631313632313933383539333932393130313631323061373536356236303430353136303230383138333033303338313532393036303430353239303530356635623834363061303031353136303230303135313531383131303135363131363933353738313835363061303031353136303230303135313832383135313831313036313136363035373631313636303631316564323536356236303230303236303230303130313531363034303531363032303031363131363739393239313930363132303731353635623630343038303531363031663139383138343033303138313532393139303532393135303630303130313631313633343536356235303630633038343031353136303430353136313136616139313833393136303230303136313230653235363562363034303531363032303831383330333033383135323930363034303532393035303630303238313630343035313631313663623931393036313231313335363562363032303630343035313830383330333831383535616661313538303135363131366536353733643566383033653364356666643562353035303530363034303531336436303166313936303166383230313136383230313830363034303532353038313031393036313137303939313930363132313265353635623934393039333530393135303530353635623630343038303531363065303831303138323532356638303832353236303630363032303830383430313832393035323833383530313832393035323831383430313833393035323834353138303836303138363532383338313532383038323031383339303532363038303835303135323834353138303836303139303935353239313834353239303833303135323930363061303832303139303831353235663630323039303931303135323930353635623633346534383762373136306530316235663532363034313630303435323630323435666664356236303430353136303830383130313630303136303031363034303162303338313131383238323130313731353631313761323537363131376132363131373663353635623630343035323930353635623630343035313630363038313031363030313630303136303430316230333831313138323832313031373135363131376132353736313137613236313137366335363562363034303830353139303831303136303031363030313630343031623033383131313832383231303137313536313137613235373631313761323631313736633536356236303430353136306530383130313630303136303031363034303162303338313131383238323130313731353631313761323537363131376132363131373663353635623630343035313630316638323031363031663139313638313031363030313630303136303430316230333831313138323832313031373135363131383336353736313138333636313137366335363562363034303532393139303530353635623566383236303166383330313132363131383464353735663830666435623831333536303031363030313630343031623033383131313135363131383636353736313138363636313137366335363562363131383739363031663832303136303166313931363630323030313631313830653536356238313831353238343630323038333836303130313131313536313138386435373566383066643562383136303230383530313630323038333031333735663931383130313630323030313931393039313532393339323530353035303536356235663630323038323834303331323135363131386239353735663830666435623831333536303031363030313630343031623033383131313135363131386365353735663830666435623631313864613834383238353031363131383365353635623934393335303530353035303536356235663630323038323834303331323135363131386632353735663830666435623530333539313930353035363562356635623833383131303135363131393133353738313831303135313833383230313532363032303031363131386662353635623530353035663931303135323536356235663831353138303834353236313139333238313630323038363031363032303836303136313138663935363562363031663031363031663139313639323930393230313630323030313932393135303530353635623630323038313532356636313139353836303230383330313834363131393162353635623933393235303530353035363562383033353630303136303031363034303162303338313136383131343631313937353537356638306664356239313930353035363562356638303566363036303834383630333132313536313139386335373566383066643562383333353932353036313139396336303230383530313631313935663536356239313530363131396161363034303835303136313139356635363562393035303932353039323530393235363562383033353630303136303031363061303162303338313136383131343631313937353537356638306664356235663630303136303031363034303162303338323131313536313139653135373631313965313631313736633536356235303630303531623630323030313930353635623566363032303830383338353033313231353631313966633537356638306664356238323335363030313630303136303430316230333830383231313135363131613132353735663830666435623930383430313930363038303832383730333132313536313161323535373566383066643562363131613264363131373830353635623832333538313532383338333031333538343832303135323631316134353630343038343031363131396233353635623630343038323031353236303630383038343031333538333831313131353631316135633537356638306664356238303835303139343530353038373630316638353031313236313161373035373566383066643562383333353631316138333631316137653832363131396339353635623631313830653536356238313831353236303035393139303931316238353031383630313930383638313031393038613833313131353631316161313537356638306664356238373837303135623833383131303135363131623361353738303335383738313131313536313161626235373566383066643562383830313830386430333630316631393031383631333135363131616366353735663830666435623631316164373631313761383536356238613832303133353839383131313135363131616537353735663830666435623631316166353866386438333836303130313631313833653536356238323532353036303430383230313335383938313131313536313162303935373566383066643562363131623137386638643833383630313031363131383365353635623863383330313532353036313162323738373833303136313139356635363562363034303832303135323834353235303931383830313931383830313631316161353536356235303630363038353031353235303931393839373530353035303530353035303530353035363562356636303430383330313633666666666666666638333531313638343532363032303830383430313531363034303630323038373031353238323831353138303835353236303630383830313931353036303230383330313934353035663932353035623830383331303135363131626139353738343531363030313630303136306130316230333136383235323933383330313933363030313932393039323031393139303833303139303631316238303536356235303936393535303530353035303530353035363562363032303831353238313531363032303832303135323566363032303833303135313630653036303430383430313532363131626461363130313030383430313832363131393162353635623930353036303430383430313531363031663139383038353834303330313630363038363031353236313162663838333833363131393162353635623932353036303031363030313630343031623033363036303837303135313136363038303836303135323630383038363031353139313530383038353834303330313630613038363031353236313163323838333833363131623465353635623932353036306130383630313531393135303830383538343033303136306330383630313532353036313163343638323832363131623465353635623931353035303630633038343031353136313163363336306530383530313832363030313630303136303430316230333136393035323536356235303933393235303530353035363562356638303630343038333835303331323135363131633763353735663830666435623832333539313530363032303833303133353830313531353831313436313163393235373566383066643562383039313530353039323530393239303530353635623566383036303430383338353033313231353631316361653537356638306664356238323335393135303631316362653630323038343031363131393566353635623930353039323530393239303530353635623566363034303832383430333132313536313163643735373566383066643562363131636466363131376361353635623930353038313335363366666666666666663831313638313134363131636634353735663830666435623831353236303230383238313031333536303031363030313630343031623033383131313135363131643066353735663830666435623833303136303166383130313835313336313164316635373566383066643562383033353631316432643631316137653832363131396339353635623831383135323630303539313930393131623832303138333031393038333831303139303837383331313135363131643462353735663830666435623932383430313932356238323834313031353631316437303537363131643631383436313139623335363562383235323932383430313932393038343031393036313164353035363562383038353837303135323530353035303530353039323931353035303536356235663630323038323834303331323135363131643930353735663830666435623831333536303031363030313630343031623033383038323131313536313164613635373566383066643562393038333031393036306530383238363033313231353631316462393537356638306664356236313164633136313137656335363562383233353831353236303230383330313335383238313131313536313164643635373566383066643562363131646532383738323836303136313138336535363562363032303833303135323530363034303833303133353832383131313135363131646639353735663830666435623631316530353837383238363031363131383365353635623630343038333031353235303631316531373630363038343031363131393566353635623630363038323031353236303830383330313335383238313131313536313165326435373566383066643562363131653339383738323836303136313163633735363562363038303833303135323530363061303833303133353832383131313135363131653530353735663830666435623631316535633837383238363031363131636337353635623630613038333031353235303631316536653630633038343031363131393566353635623630633038323031353239353934353035303530353035303536356238323831353236303430363032303832303135323566363131386461363034303833303138343631313931623536356236333465343837623731363065303162356635323630313136303034353236303234356666643562383138313033383138313131313536313134653935373631313465393631316539343536356238303832303238313135383238323034383431343137363131346539353736313134653936313165393435363562363334653438376237313630653031623566353236303332363030343532363032343566666435623830383230313830383231313135363131346539353736313134653936313165393435363562356638363531363131663061383138343630323038623031363131386639353635623630653038373930316236303031363030313630653031623033313931363930383330313930383135323835353136313166333238313630303438343031363032303861303136313138663935363562383535313931303139303631316634383831363030343834303136303230383930313631313866393536356236306330393439303934316236303031363030313630633031623033313931363630303439313930393430313930383130313933393039333532353035303630306330313934393335303530353035303536356236336666666666666666383138313136383338323136303139303830383231313135363130623539353736313062353936313165393435363562363366666666666666663831383131363833383231363032383038323136393139303832383131343631316661663537363131666166363131653934353635623530353039323931353035303536356236316666666636306630316238613630663031623136383135323566363366666666666666663630653031623830386236306530316231363630303238343031353238393630303638343031353238303839363065303162313636303236383430313532353038363531363131666664383136303261383530313630323038623031363131386639353635623836353139303833303139303631323031343831363032613834303136303230386230313631313866393536356236306330383739303162363030313630303136306330316230333139313636303261393239303931303139313832303135323631323034363630333238323031383636306530316236303031363030313630653031623033313931363930353235363562363132303566363033363832303138353630653031623630303136303031363065303162303331393136393035323536356236303361303139623961353035303530353035303530353035303530353035303536356235663833353136313230383238313834363032303838303136313138663935363562363036303933393039333162366266666666666666666666666666666666666666666666666631393136393139303932303139303831353236303134303139323931353035303536356235663834353136313230623838313834363032303839303136313138663935363562363030313630303136306530316230333139363065303935383631623831313639313930393330313930383135323932393039333162313636303034383230313532363030383031393239313530353035363562356638333531363132306633383138343630323038383031363131386639353635623630633039333930393331623630303136303031363063303162303331393136393139303932303139303831353236303038303139323931353035303536356235663832353136313231323438313834363032303837303136313138663935363562393139303931303139323931353035303536356235663630323038323834303331323135363132313365353735663830666435623530353139313930353035366665613136343733366636633633343330303038313930303061222c0a2020202020202020202020202262616c616e6365223a2022307830222c0a202020202020202020202020226e6f6e6365223a2022307831220a20202020202020207d2c0a20202020202020202263306666656531323334353637383930616263646566313233343536373839306162636465663334223a207b0a20202020202020202020202022636f6465223a202230783630383036303430353236303034333631303631303037623537363030303335363065303163383036333936323336303964313136313030346535373830363339363233363039643134363130313131353738303633393961383865633431343631303132343537383036336632666465333862313436313031343435373830363366336237646561643134363130313634353736303030383066643562383036333230346531633761313436313030383035373830363337313530313861363134363130306263353738303633376566663237356531343631303064333537383036333864613563623562313436313030663335373562363030303830666435623334383031353631303038633537363030303830666435623530363130306130363130303962333636303034363130343939353635623631303138343536356236303430353136303031363030313630613031623033393039313136383135323630323030313630343035313830393130333930663335623334383031353631303063383537363030303830666435623530363130306431363130323135353635623030356233343830313536313030646635373630303038306664356235303631303064313631303065653336363030343631303462643536356236313032323935363562333438303135363130306666353736303030383066643562353036303030353436303031363030313630613031623033313636313030613035363562363130306431363130313166333636303034363130353063353635623631303239313536356233343830313536313031333035373630303038306664356235303631303064313631303133663336363030343631303462643536356236313033303035363562333438303135363130313530353736303030383066643562353036313030643136313031356633363630303436313034393935363562363130333336353635623334383031353631303137303537363030303830666435623530363130306130363130313766333636303034363130343939353635623631303362343536356236303030383036303030383336303031363030313630613031623033313636303430353136313031616139303633356336306461316236306530316238313532363030343031393035363562363030303630343035313830383330333831383535616661393135303530336438303630303038313134363130316535353736303430353139313530363031663139363033663364303131363832303136303430353233643832353233643630303036303230383430313365363130316561353635623630363039313530356235303931353039313530383136313031663935373630303038306664356238303830363032303031393035313831303139303631303230643931393036313035653235363562393439333530353035303530353635623631303231643631303364613536356236313032323736303030363130343334353635623536356236313032333136313033646135363562363034303531363330386632383339373630653431623831353236303031363030313630613031623033383238313136363030343833303135323833313639303633386632383339373039303630323430313562363030303630343035313830383330333831363030303837383033623135383031353631303237353537363030303830666435623530356166313135383031353631303238393537336436303030383033653364363030306664356235303530353035303530353035363562363130323939363130336461353635623630343035313633323738663739343336306531316238313532363030313630303136306130316230333834313639303633346631656632383639303334393036313032633939303836393038363930363030343031363130356666353635623630303036303430353138303833303338313835383838303362313538303135363130326532353736303030383066643562353035616631313538303135363130326636353733643630303038303365336436303030666435623530353035303530353035303530353035363562363130333038363130336461353635623630343035313633316232636537663336306531316238313532363030313630303136306130316230333832383131363630303438333031353238333136393036333336353963666536393036303234303136313032356235363562363130333365363130336461353635623630303136303031363061303162303338313136363130336138353736303430353136323436316263643630653531623831353236303230363030343832303135323630323636303234383230313532376634663737366536313632366336353361323036653635373732303666373736653635373232303639373332303734363836353230376136353732366632303631363034343832303135323635363436343732363537333733363064303162363036343832303135323630383430313562363034303531383039313033393066643562363130336231383136313034333435363562353035363562363030303830363030303833363030313630303136306130316230333136363034303531363130316161393036333033653134363931363065363162383135323630303430313930353635623630303035343630303136303031363061303162303331363333313436313032323735373630343035313632343631626364363065353162383135323630323036303034383230313831393035323630323438323031353237663466373736653631363236633635336132303633363136633663363537323230363937333230366536663734323037343638363532303666373736653635373236303434383230313532363036343031363130333966353635623630303038303534363030313630303136306130316230333833383131363630303136303031363061303162303331393833313638313137383435353630343035313931393039323136393238333931376638626530303739633533313635393134313334346364316664306134663238343139343937663937323261336461616665336234313836663662363435376530393139306133353035303536356236303031363030313630613031623033383131363831313436313033623135373630303038306664356236303030363032303832383430333132313536313034616235373630303038306664356238313335363130346236383136313034383435363562393339323530353035303536356236303030383036303430383338353033313231353631303464303537363030303830666435623832333536313034646238313631303438343536356239313530363032303833303133353631303465623831363130343834353635623830393135303530393235303932393035303536356236333465343837623731363065303162363030303532363034313630303435323630323436303030666435623630303038303630303036303630383438363033313231353631303532313537363030303830666435623833333536313035326338313631303438343536356239323530363032303834303133353631303533633831363130343834353635623931353036303430383430313335363766666666666666666666666666666666383038323131313536313035353935373630303038306664356238313836303139313530383636303166383330313132363130353664353736303030383066643562383133353831383131313135363130353766353736313035376636313034663635363562363034303531363031663832303136303166313939303831313636303366303131363831303139303833383231313831383331303137313536313035613735373631303561373631303466363536356238313630343035323832383135323839363032303834383730313031313131353631303563303537363030303830666435623832363032303836303136303230383330313337363030303630323038343833303130313532383039353530353035303530353035303932353039323530393235363562363030303630323038323834303331323135363130356634353736303030383066643562383135313631303462363831363130343834353635623630303138303630613031623033383331363831353236303030363032303630343036303230383430313532383335313830363034303835303135323630303035623831383131303135363130363363353738353831303138333031353138353832303136303630303135323832303136313036323035363562353036303030363036303832383630313031353236303630363031663139363031663833303131363835303130313932353035303530393339323530353035303536666561323634363937303636373335383232313232303139663339393833613666643135663363666661373634656664366662303233346666653864373130353162336562646463306236626439396638376661393736343733366636633633343330303038313930303333222c0a2020202020202020202020202273746f72616765223a207b0a2020202020202020202020202020202022307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030223a2022307830303030303030303030303030303030303030303030303038646239376337636563653234396332623938626463303232366363346332613537626635326663220a2020202020202020202020207d2c0a2020202020202020202020202262616c616e6365223a2022307830222c0a202020202020202020202020226e6f6e6365223a2022307831220a20202020202020207d0a202020207d2c0a202020202261697264726f7048617368223a2022307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c0a202020202261697264726f70416d6f756e74223a206e756c6c2c0a20202020226e756d626572223a2022307830222c0a202020202267617355736564223a2022307830222c0a2020202022706172656e7448617368223a2022307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c0a202020202262617365466565506572476173223a206e756c6c2c0a2020202022657863657373426c6f62476173223a206e756c6c2c0a2020202022626c6f6247617355736564223a206e756c6c0a7d0000000a000000010000000000000002000000090000000151f9b7d0d57976387d7383be36a37b83639b634f6404caa6a76c49d53edd3bfa123bf20951643a8e17a1541b3f8f0cfbf19fdaa3e91e51f02c6b2a8b5d5170bd01000000090000000151f9b7d0d57976387d7383be36a37b83639b634f6404caa6a76c49d53edd3bfa123bf20951643a8e17a1541b3f8f0cfbf19fdaa3e91e51f02c6b2a8b5d5170bd01b955cfdf" + + +addValidators=($addValidator1 $addValidator2 $addValidator3 $addValidator4 $addValidator5) + +curl -X POST --data '{ + "jsonrpc": "2.0", + "method": "platform.issueTx", + "params": { + "tx":"'${createSubnet}'", + "encoding": "hex" + }, + "id": 1 +}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P + + +sleep 1 + +for v in "${addValidators[@]}"; do + curl -X POST --data '{ + "jsonrpc": "2.0", + "method": "platform.issueTx", + "params": { + "tx":"'${v}'", + "encoding": "hex" + }, + "id": 1 + }' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P + sleep 3 +done + +sleep 3 + + +curl -X POST --data '{ + "jsonrpc": "2.0", + "method": "platform.issueTx", + "params": { + "tx":"'${createChain}'", + "encoding": "hex" + }, + "id": 1 +}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P + + +#0x479f66c8be895830547e70b4b298cafd433dba6e1ebdfb85 +#0xde31b4d8b22991d51aa6aa1fc733f23a851a8c9427f780c8 +#0xe9094f73698002fd52c90819b457b9fbc866ab80727952c7 +#0xaa18d3991cf637aa6c162f5e95cf163f69cd82913f89fa2e +#0xf29bce5f34a74301eb0de716d5194e4a4aea5d7ae0cb875e + + + diff --git a/scripts/run_simplex.sh b/scripts/run_simplex.sh new file mode 100644 index 000000000000..442e6bc178a6 --- /dev/null +++ b/scripts/run_simplex.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -euo pipefail + +AVALANCHE_BINARY="${AVALANCHE_BINARY:-./build/avalanchego} --network-no-ingress-connections-grace-period 9000m --log-level trace --track-subnets=BKBZ6xXTnT86B4L5fp8rvtcmNSpvtNz8En9jG61ywV2uWyeHy" + +pgrep avalanchego && pkill avalanchego + +rm -rf local* +rm -rf *.wal + +mkdir -p ./local1/plugins +mkdir -p ./local2/plugins +mkdir -p ./local3/plugins +mkdir -p ./local4/plugins +mkdir -p ./local5/plugins + +ln /Users/yacov.manevich/.avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy ./local1/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy +ln /Users/yacov.manevich/.avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy ./local2/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy +ln /Users/yacov.manevich/.avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy ./local3/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy +ln /Users/yacov.manevich/.avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy ./local4/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy +ln /Users/yacov.manevich/.avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy ./local5/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy + +sleep 3 + +${AVALANCHE_BINARY} --data-dir ./local1 --network-id=local --db-dir=./local1 --public-ip=127.0.0.1 --http-port=9650 --staking-port=9651 --staking-tls-cert-file=./staking/local/staker1.crt --staking-tls-key-file=./staking/local/staker1.key --staking-signer-key-file=./staking/local/signer1.key --bootstrap-ips="" --bootstrap-ids="" &> node1.log & +${AVALANCHE_BINARY} --data-dir ./local2 --network-id=local --db-dir=./local2 --public-ip=127.0.0.1 --http-port=9660 --staking-port=9661 --staking-tls-cert-file=./staking/local/staker2.crt --staking-tls-key-file=./staking/local/staker2.key --staking-signer-key-file=./staking/local/signer2.key --bootstrap-ips="127.0.0.1:9651" --bootstrap-ids="NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg" &> node2.log & +${AVALANCHE_BINARY} --data-dir ./local3 --network-id=local --db-dir=./local3 --public-ip=127.0.0.1 --http-port=9670 --staking-port=9671 --staking-tls-cert-file=./staking/local/staker3.crt --staking-tls-key-file=./staking/local/staker3.key --staking-signer-key-file=./staking/local/signer3.key --bootstrap-ips="127.0.0.1:9651" --bootstrap-ids="NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg" &> node3.log & +${AVALANCHE_BINARY} --data-dir ./local4 --network-id=local --db-dir=./local4 --public-ip=127.0.0.1 --http-port=9680 --staking-port=9681 --staking-tls-cert-file=./staking/local/staker4.crt --staking-tls-key-file=./staking/local/staker4.key --staking-signer-key-file=./staking/local/signer4.key --bootstrap-ips="127.0.0.1:9651" --bootstrap-ids="NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg" &> node4.log & +${AVALANCHE_BINARY} --data-dir ./local5 --network-id=local --db-dir=./local5 --public-ip=127.0.0.1 --bootstrap-ips="127.0.0.1:9651" --bootstrap-ids="NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg" --http-port=9690 --staking-port=9691 --staking-tls-cert-file=./staking/local/staker5.crt --staking-tls-key-file=./staking/local/staker5.key --staking-signer-key-file=./staking/local/signer5.key &> node5.log & + + + diff --git a/scripts/simplex/README.md b/scripts/simplex/README.md new file mode 100644 index 000000000000..bc11027fdfbf --- /dev/null +++ b/scripts/simplex/README.md @@ -0,0 +1,144 @@ +# Simplex Local Network + +Run a local 5-node AvalancheGo network with a Simplex-consensus L1 chain and a web dashboard for sending transactions. + +## Prerequisites + +- Go toolchain +- Node.js / npm +- Python 3 + +## Quick Start + +### 1. Clean up any previous network + +```bash +./scripts/simplex/clean.sh +``` + +Kills leftover AvalancheGo processes, removes tmpnet data, and deletes generated files. + +### 2. Build AvalancheGo + +```bash +./scripts/build.sh +``` + +### 3. Build the subnet-evm plugin + +```bash +mkdir -p ~/.avalanchego/plugins +go build -o ~/.avalanchego/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy ./graft/subnet-evm/plugin/ +``` + +Compiles the subnet-evm binary and installs it as a plugin. The plugin ID (`srEX...`) is the VM ID that the L1 chain will reference. + +Note: If running `start-network` you see `failed to register VM ... RPCChainVM protocol version mismatch between AvalancheGo and Virtual Machine plugin`, delete the old plugins with `rm -rf ~/.avalanchego/plugins` and re-run this step. + +### 4. Start the network + +```bash +./scripts/run_tmpnetctl.sh start-network --node-count=5 --avalanchego-path=./build/avalanchego +``` + +Starts a 5-node local network. Nodes get dynamic ports assigned automatically. + +### 5. Create the L1 + +```bash +./scripts/simplex/create_l1.sh +``` + +Issues three P-Chain transactions (CreateSubnet, CreateChain, ConvertSubnetToL1), injects BLS keys into the Simplex config, restarts the network, and writes chain metadata to `tx-frontend/public/chains.json`. + +To use Snowball consensus instead of Simplex: + +```bash +./scripts/simplex/create_l1.sh ./scripts/simplex/config/subnet_config.json +``` + +### 6. Fund accounts + +```bash +./scripts/simplex/fund_nodes.sh +``` + +Syncs node URIs from the tmpnet directory, generates a deterministic address per node, and funds each with 100 AVAX from the pre-funded ewoq key on both the C-Chain and L1 chain. Writes node info to `tx-frontend/public/nodes.json`. + +### 7. Start the dashboard + +```bash +cd scripts/simplex/tx-frontend +npm install # first time only +npm run dev +``` + +Open `http://localhost:3000`. + +### 8. Stop or clean + +```bash +# Stop the network (keeps data): +./scripts/simplex/stop_network.sh + +# Full cleanup (kills processes, removes tmpnet data, deletes generated files): +./scripts/simplex/clean.sh +``` + +## Verifying + +Check node health: + +```bash +./scripts/simplex/check_network.sh +``` + +## Sending a test transaction + +`send_tx` is a Go tool that sends 1 AVAX on the L1 chain using the pre-funded ewoq key. It connects to the first node's RPC endpoint, sends the transaction, and waits up to 30 seconds for it to be mined. + +```bash +go run ./scripts/simplex/send_tx/ \ + --nodes=scripts/simplex/tx-frontend/public/nodes.json \ + --chains=scripts/simplex/tx-frontend/public/chains.json +``` + +It will print the chain ID, ewoq balance, current block number, and the transaction hash once confirmed. + +## Dashboard Features + +- **Node cards** -- health status, block height, balance, and NodeID for each node. Click a card to route API calls through that node. +- **Chain selector** -- switch between P-Chain, C-Chain, and L1 chains. +- **P-Chain view** -- shows P-Chain blocks with transaction type badges (CreateSubnet, CreateChain, ConvertSubnetToL1, etc.). +- **Send transactions** -- pick a sender and receiver node, enter an amount, and send AVAX on any EVM chain. +- **Live updates** -- balances, block heights, and transaction history refresh automatically. + +## File Structure + +``` +scripts/simplex/ + clean.sh # Kill processes, remove tmpnet data + stop_network.sh # Stop the network + check_network.sh # Health-check all nodes + fund_nodes.sh # Sync node URIs + fund accounts + create_l1.sh # Create L1 (subnet + chain + convert) + create_l1/main.go # Go tool for L1 creation + send_tx/main.go # Go tool for test transactions + config/ + genesis.json # Subnet-EVM chain genesis + subnet_config_simplex.json # Simplex consensus parameters (default) + subnet_config.json # Snowball consensus parameters (alternative) + tx-frontend/ # Next.js dashboard + public/ + nodes.json # Generated: node URIs + addresses + chains.json # Generated: chain metadata + app/ + page.tsx # Dashboard UI +``` + +## Configuration + +- **`config/subnet_config_simplex.json`** -- Simplex consensus parameters (used by default). +- **`config/subnet_config.json`** -- Snowball consensus parameters (pass as argument to `create_l1.sh` to use). +- **`config/genesis.json`** -- Subnet-EVM genesis. Pre-funds the ewoq address (`0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC`). +- Node accounts use deterministic keys derived from `sha256("simplex-node-{i}")`. diff --git a/scripts/simplex/check_network.sh b/scripts/simplex/check_network.sh new file mode 100755 index 000000000000..88880adb4917 --- /dev/null +++ b/scripts/simplex/check_network.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -euo pipefail + +NETWORK_DIR="${TMPNET_NETWORK_DIR:-$HOME/.tmpnet/networks/latest}" + +echo "Checking nodes in ${NETWORK_DIR}..." +echo + +for node_dir in "${NETWORK_DIR}"/NodeID-*; do + [ -d "$node_dir" ] || continue + node_id=$(basename "$node_dir") + process_file="${node_dir}/process.json" + + if [ ! -f "$process_file" ]; then + echo "${node_id}: no process.json found" + continue + fi + + uri=$(python3 -c "import json; print(json.load(open('${process_file}'))['uri'])" 2>/dev/null || true) + if [ -z "$uri" ]; then + echo "${node_id}: could not read URI" + continue + fi + + echo "${node_id} (${uri}):" + curl -s -X POST --data '{"jsonrpc":"2.0","id":1,"method":"info.getNodeID"}' -H 'content-type:application/json' "${uri}/ext/info" | python3 -m json.tool + echo +done diff --git a/scripts/simplex/clean.sh b/scripts/simplex/clean.sh new file mode 100755 index 000000000000..bb759b3ead7c --- /dev/null +++ b/scripts/simplex/clean.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + +echo "Cleaning up previous network..." + +# Stop the network gracefully via tmpnetctl if possible +NETWORK_DIR="${HOME}/.tmpnet/networks/latest" +if [ -f "${NETWORK_DIR}/network.env" ]; then + source "${NETWORK_DIR}/network.env" + NETWORK_DIR="${TMPNET_NETWORK_DIR:-${NETWORK_DIR}}" +fi + +AVALANCHE_PATH=$(cd "${SCRIPT_DIR}/../.." && pwd) +if [ -f "${AVALANCHE_PATH}/build/tmpnetctl" ]; then + "${AVALANCHE_PATH}/build/tmpnetctl" stop-network --network-dir="${NETWORK_DIR}" 2>/dev/null || true +fi + +# Kill any remaining AvalancheGo processes +if pgrep -f "avalanchego" > /dev/null 2>&1; then + echo "Killing remaining AvalancheGo processes..." + pkill -f "avalanchego" 2>/dev/null || true + sleep 1 +fi + +# Remove tmpnet network data +if [ -d "${HOME}/.tmpnet/networks" ]; then + echo "Removing tmpnet network data..." + rm -rf "${HOME}/.tmpnet/networks" +fi + +# Clean generated frontend files +rm -f "${SCRIPT_DIR}/tx-frontend/public/nodes.json" +rm -f "${SCRIPT_DIR}/tx-frontend/public/chains.json" +rm -f "${SCRIPT_DIR}/config/.resolved_subnet_config.json" + +echo "Clean complete." diff --git a/scripts/simplex/config/.resolved_subnet_config.json b/scripts/simplex/config/.resolved_subnet_config.json new file mode 100644 index 000000000000..730d224001d7 --- /dev/null +++ b/scripts/simplex/config/.resolved_subnet_config.json @@ -0,0 +1,28 @@ +{ + "simplexParameters": { + "initialValidators": [ + { + "nodeID": "NodeID-4m5BMMCbEKr6fuKmF975T9yk7vFkQ5wXT", + "publicKey": "rqjqeKB2iYk9kdyWZ6aESTdBQP7LDepAPhiLn5gC3OqoJLF9wzU0SkG3smlvMrlr" + }, + { + "nodeID": "NodeID-A75Xzm7VnKSnMUavpVcfCVevBTc7jTpwW", + "publicKey": "jEbRMuoHFQL+3rEU/9cO6d9n24Racnhd+cLpAf4OYY5VWFSbCL8Ko+8P0QZNfLoO" + }, + { + "nodeID": "NodeID-ENrh51L8AtYAcgatVffo7JrNEP4JtY7qi", + "publicKey": "lLeLl9rFjRsYWJgrX6ABBCPouDuj5O4bRaqHuchXsLWg3HUlaBJGTfrtFk+dd80t" + }, + { + "nodeID": "NodeID-F7fBsbT8ptGANV9zC8taw5vpCCCZ7fRua", + "publicKey": "iwhLwoN0bWcma7iyWS5WpXWYCTfpN43NN7yQoTVCWdVGV2Ivcu3HQq/ZeDPdiIW6" + }, + { + "nodeID": "NodeID-HBzyshYfEqtgS8T5sWbfB3ZvpDXyKH9uk", + "publicKey": "lm1YZVa4+htLUn7FKGUv+JM6HUMEMbBcKnAjPoXN3FuwwvCD/ux5nDOrXyW2qw8a" + } + ], + "maxNetworkDelay": 5000000000, + "maxRebroadcastWait": 5000000000 + } +} \ No newline at end of file diff --git a/scripts/simplex/config/genesis.json b/scripts/simplex/config/genesis.json new file mode 100644 index 000000000000..857bbb0a97f2 --- /dev/null +++ b/scripts/simplex/config/genesis.json @@ -0,0 +1,30 @@ +{ + "config": { + "chainId": 99999, + "feeConfig": { + "gasLimit": 200000000, + "minBaseFee": 1000000000, + "targetGas": 400000000, + "baseFeeChangeDenominator": 48, + "minBlockGasCost": 0, + "maxBlockGasCost": 10000000, + "targetBlockRate": 2, + "blockGasCostStep": 500000 + } + }, + "alloc": { + "8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": { + "balance": "0x52B7D2DCC80CD2E4000000" + } + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x00", + "gasLimit": "0xBEBC200", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} diff --git a/scripts/simplex/config/subnet_config.json b/scripts/simplex/config/subnet_config.json new file mode 100644 index 000000000000..c7a23ed1cd89 --- /dev/null +++ b/scripts/simplex/config/subnet_config.json @@ -0,0 +1,12 @@ +{ + "snowParameters": { + "k": 20, + "alphaPreference": 15, + "alphaConfidence": 15, + "beta": 20, + "concurrentRepolls": 4, + "optimalProcessing": 10, + "maxOutstandingItems": 256, + "maxItemProcessingTime": 30000000000 + } +} diff --git a/scripts/simplex/config/subnet_config_simplex.json b/scripts/simplex/config/subnet_config_simplex.json new file mode 100644 index 000000000000..af33424ba3fe --- /dev/null +++ b/scripts/simplex/config/subnet_config_simplex.json @@ -0,0 +1,6 @@ +{ + "simplexParameters": { + "maxNetworkDelay": 5000000000, + "maxRebroadcastWait": 5000000000 + } +} diff --git a/scripts/simplex/create_l1.sh b/scripts/simplex/create_l1.sh new file mode 100755 index 000000000000..8e1531820ef6 --- /dev/null +++ b/scripts/simplex/create_l1.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +AVALANCHE_PATH=$(cd "${SCRIPT_DIR}/../.." && pwd) +cd "${AVALANCHE_PATH}" + +NETWORK_DIR="${HOME}/.tmpnet/networks/latest" +if [ -f "${NETWORK_DIR}/network.env" ]; then + source "${NETWORK_DIR}/network.env" + NETWORK_DIR="${TMPNET_NETWORK_DIR:-${NETWORK_DIR}}" +fi + +SUBNET_CONFIG="${1:-${SCRIPT_DIR}/config/subnet_config_simplex.json}" + +echo "Creating L1..." +go run ./scripts/simplex/create_l1/ \ + --network-dir="${NETWORK_DIR}" \ + --genesis="${SCRIPT_DIR}/config/genesis.json" \ + --config="${SUBNET_CONFIG}" \ + --chains-output="${SCRIPT_DIR}/tx-frontend/public/chains.json" \ + --resolved-config-output="${SCRIPT_DIR}/config/.resolved_subnet_config.json" diff --git a/scripts/simplex/create_l1/BUILD.bazel b/scripts/simplex/create_l1/BUILD.bazel new file mode 100644 index 000000000000..76be04a87a56 --- /dev/null +++ b/scripts/simplex/create_l1/BUILD.bazel @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "create_l1_lib", + srcs = ["main.go"], + importpath = "github.com/ava-labs/avalanchego/scripts/simplex/create_l1", + visibility = ["//visibility:private"], + deps = [ + "//api/info", + "//config", + "//genesis", + "//ids", + "//tests/fixture/tmpnet", + "//utils/constants", + "//utils/logging", + "//utils/units", + "//vms/platformvm/txs", + "//vms/platformvm/warp/message", + "//vms/secp256k1fx", + "//wallet/subnet/primary", + ], +) + +go_binary( + name = "create_l1", + embed = [":create_l1_lib"], + visibility = ["//visibility:public"], +) diff --git a/scripts/simplex/create_l1/main.go b/scripts/simplex/create_l1/main.go new file mode 100644 index 000000000000..aa04830f2415 --- /dev/null +++ b/scripts/simplex/create_l1/main.go @@ -0,0 +1,447 @@ +package main + +import ( + "context" + "encoding/base64" + "encoding/json" + "flag" + "fmt" + "log" + "os" + "path/filepath" + "time" + + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/config" + "github.com/ava-labs/avalanchego/genesis" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" +) + +type l1Result struct { + SubnetID ids.ID + ChainID ids.ID + ConvertTxID ids.ID + ConversionID ids.ID + ChainName string + ConsensusType string + Validators []*txs.ConvertSubnetToL1Validator + ConversionValidators []message.SubnetToL1ConversionValidatorData + ResolvedConfigBytes []byte +} + +func main() { + networkDir := flag.String("network-dir", os.Getenv(tmpnet.NetworkDirEnvName), "Path to tmpnet network directory") + genesisPath := flag.String("genesis", "", "Path to chain genesis JSON file") + chainsOutput := flag.String("chains-output", "", "Path to write chains.json for the frontend") + configPath := flag.String("config", "", "Path to subnet config JSON file") + resolvedConfigOutput := flag.String("resolved-config-output", "", "Path to write the resolved subnet config (with validators populated)") + chainName := flag.String("name", "simplexl1", "Name for the L1 chain") + flag.Parse() + + if *networkDir == "" { + log.Fatal("--network-dir or TMPNET_NETWORK_DIR is required") + } + if *genesisPath == "" { + log.Fatal("--genesis is required") + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + // Load the running tmpnet network from disk + logger := logging.NewLogger("", logging.NewWrappedCore(logging.Info, os.Stderr, logging.Plain.ConsoleEncoder())) + network, err := tmpnet.ReadNetwork(ctx, logger, *networkDir) + if err != nil { + log.Fatalf("failed to read network: %s", err) + } + if len(network.Nodes) == 0 { + log.Fatal("no nodes found in network") + } + + // Read the subnet config and determine consensus type + consensusType, configBytes := readSubnetConfig(*configPath) + + genesisBytes, err := os.ReadFile(*genesisPath) + if err != nil { + log.Fatalf("failed to read genesis file: %s", err) + } + + // Issue P-Chain transactions to create the subnet, chain, and convert to L1 + result := createL1(ctx, network, genesisBytes, configBytes, consensusType, *chainName) + + // Write the resolved config (with validators injected) to disk + if *resolvedConfigOutput != "" && result.ResolvedConfigBytes != nil { + if err := os.WriteFile(*resolvedConfigOutput, result.ResolvedConfigBytes, 0o644); err != nil { + log.Fatalf("failed to write resolved config: %s", err) + } + log.Printf("wrote resolved config to %s", *resolvedConfigOutput) + } + + printResult(result) + + // Update each node's flags to track the new subnet, then restart + configureAndRestartNodes(ctx, network, result) + + // Write chain metadata for the frontend + if *chainsOutput != "" { + writeChainsJSON(*chainsOutput, result) + } +} + +// readSubnetConfig reads the subnet config file and determines the consensus +// type based on which parameter field is present (simplexParameters vs snowParameters). +func readSubnetConfig(configPath string) (string, []byte) { + if configPath == "" { + return "snowman", nil + } + + configBytes, err := os.ReadFile(configPath) + if err != nil { + log.Fatalf("failed to read config file: %s", err) + } + + var configData map[string]json.RawMessage + if err := json.Unmarshal(configBytes, &configData); err != nil { + log.Fatalf("failed to parse config file: %s", err) + } + + consensusType := "snowman" + if _, ok := configData["simplexParameters"]; ok { + consensusType = "simplex" + } else if _, ok := configData["snowParameters"]; ok { + consensusType = "snowball" + } else if _, ok := configData["consensusParameters"]; ok { + consensusType = "snowball" + } + + log.Printf("consensus type: %s", consensusType) + return consensusType, configBytes +} + +// createL1 issues three P-Chain transactions: +// 1. CreateSubnetTx — creates a new subnet owned by the ewoq key +// 2. CreateChainTx — creates a subnet-evm chain on that subnet +// 3. ConvertSubnetToL1Tx — converts the subnet to an L1 with all network nodes as validators +// +// For simplex configs, it also injects each node's BLS key into the config as initialValidators. +func createL1( + ctx context.Context, + network *tmpnet.Network, + genesisBytes []byte, + configBytes []byte, + consensusType string, + chainName string, +) l1Result { + apiURI := network.Nodes[0].GetAccessibleURI() + log.Printf("using node API: %s", apiURI) + + kc := secp256k1fx.NewKeychain(genesis.EWOQKey) + + // Create a P-Chain wallet using the pre-funded ewoq key + log.Println("creating P-chain wallet...") + pWallet, err := primary.MakePWallet(ctx, apiURI, kc, primary.WalletConfig{}) + if err != nil { + log.Fatalf("failed to create wallet: %s", err) + } + + // Create a new subnet owned by the ewoq key + log.Println("issuing CreateSubnetTx...") + subnetTx, err := pWallet.IssueCreateSubnetTx( + &secp256k1fx.OutputOwners{ + Threshold: 1, + Addrs: []ids.ShortID{genesis.EWOQKey.Address()}, + }, + ) + if err != nil { + log.Fatalf("failed to create subnet: %s", err) + } + subnetID := subnetTx.ID() + log.Printf("created subnet: %s", subnetID) + + // Re-create wallet so it tracks the new subnet's UTXO state + pWallet, err = primary.MakePWallet(ctx, apiURI, kc, primary.WalletConfig{ + SubnetIDs: []ids.ID{subnetID}, + }) + if err != nil { + log.Fatalf("failed to recreate wallet with subnet: %s", err) + } + + // Create a subnet-evm chain on the subnet + log.Println("issuing CreateChainTx...") + chainTx, err := pWallet.IssueCreateChainTx( + subnetID, + genesisBytes, + constants.SubnetEVMID, + nil, + chainName, + ) + if err != nil { + log.Fatalf("failed to create chain: %s", err) + } + chainID := chainTx.ID() + log.Printf("created chain: %s", chainID) + + // Collect each node's BLS key for use as L1 validators + validators, conversionValidators := collectValidators(ctx, network) + + // For simplex, inject node BLS keys as initialValidators in the config + resolvedConfigBytes := resolveConfig(configBytes, consensusType, conversionValidators) + + // Convert the subnet to an L1 with all nodes as genesis validators + log.Println("issuing ConvertSubnetToL1Tx...") + address := []byte{} + convertTx, err := pWallet.IssueConvertSubnetToL1Tx(subnetID, chainID, address, validators) + if err != nil { + log.Fatalf("failed to convert subnet to L1: %s", err) + } + + conversionID, err := message.SubnetToL1ConversionID(message.SubnetToL1ConversionData{ + SubnetID: subnetID, + ManagerChainID: chainID, + ManagerAddress: address, + Validators: conversionValidators, + }) + if err != nil { + log.Fatalf("failed to calculate conversion ID: %s", err) + } + + return l1Result{ + SubnetID: subnetID, + ChainID: chainID, + ConvertTxID: convertTx.ID(), + ConversionID: conversionID, + ChainName: chainName, + ConsensusType: consensusType, + Validators: validators, + ConversionValidators: conversionValidators, + ResolvedConfigBytes: resolvedConfigBytes, + } +} + +// collectValidators queries each node's info API for its NodeID and BLS proof +// of possession, and builds the validator structs needed for ConvertSubnetToL1Tx. +func collectValidators( + ctx context.Context, + network *tmpnet.Network, +) ([]*txs.ConvertSubnetToL1Validator, []message.SubnetToL1ConversionValidatorData) { + log.Println("collecting node BLS keys...") + + var validators []*txs.ConvertSubnetToL1Validator + var conversionValidators []message.SubnetToL1ConversionValidatorData + + for _, node := range network.Nodes { + nodeURI := node.GetAccessibleURI() + infoClient := info.NewClient(nodeURI) + + nodeID, nodePoP, err := infoClient.GetNodeID(ctx) + if err != nil { + log.Fatalf("failed to get node ID from %s: %s", nodeURI, err) + } + + log.Printf(" node %s: BLS key collected", nodeID) + + // All validators get equal weight so they have equal voting power in consensus. + validators = append(validators, &txs.ConvertSubnetToL1Validator{ + NodeID: nodeID.Bytes(), + Weight: units.Schmeckle, + Balance: units.Avax, + Signer: *nodePoP, + RemainingBalanceOwner: message.PChainOwner{}, + DeactivationOwner: message.PChainOwner{}, + }) + + conversionValidators = append(conversionValidators, message.SubnetToL1ConversionValidatorData{ + NodeID: nodeID.Bytes(), + BLSPublicKey: nodePoP.PublicKey, + Weight: units.Schmeckle, + }) + } + + return validators, conversionValidators +} + +// resolveConfig takes the raw subnet config bytes and, for simplex configs, +// injects the initialValidators field with each node's NodeID and BLS public key. +// For non-simplex configs, it returns the bytes unchanged. +func resolveConfig( + configBytes []byte, + consensusType string, + conversionValidators []message.SubnetToL1ConversionValidatorData, +) []byte { + if configBytes == nil { + return nil + } + + if consensusType != "simplex" { + return configBytes + } + + var configMap map[string]json.RawMessage + if err := json.Unmarshal(configBytes, &configMap); err != nil { + log.Fatalf("failed to parse config: %s", err) + } + + var simplexParams map[string]json.RawMessage + if err := json.Unmarshal(configMap["simplexParameters"], &simplexParams); err != nil { + log.Fatalf("failed to parse simplexParameters: %s", err) + } + + type validatorEntry struct { + NodeID ids.NodeID `json:"nodeID"` + PublicKey []byte `json:"publicKey"` + } + + var initialValidators []validatorEntry + for _, cv := range conversionValidators { + nodeID, err := ids.ToNodeID(cv.NodeID) + if err != nil { + log.Fatalf("failed to convert nodeID: %s", err) + } + initialValidators = append(initialValidators, validatorEntry{ + NodeID: nodeID, + PublicKey: cv.BLSPublicKey[:], + }) + } + + ivBytes, err := json.Marshal(initialValidators) + if err != nil { + log.Fatalf("failed to marshal initialValidators: %s", err) + } + simplexParams["initialValidators"] = ivBytes + + spBytes, err := json.Marshal(simplexParams) + if err != nil { + log.Fatalf("failed to marshal simplexParameters: %s", err) + } + configMap["simplexParameters"] = spBytes + + resolved, err := json.MarshalIndent(configMap, "", " ") + if err != nil { + log.Fatalf("failed to marshal resolved config: %s", err) + } + return resolved +} + +// configureAndRestartNodes sets track-subnets and subnet-config-content flags +// on each node, writes the config to disk, and restarts the network. +func configureAndRestartNodes(ctx context.Context, network *tmpnet.Network, result l1Result) { + log.Println("configuring nodes to track subnet...") + + // Build the base64-encoded subnet config content that nodes need + subnetConfigContent := "" + if result.ResolvedConfigBytes != nil { + configMap := map[string]json.RawMessage{ + result.SubnetID.String(): result.ResolvedConfigBytes, + } + marshaledConfigs, err := json.Marshal(configMap) + if err != nil { + log.Fatalf("failed to marshal subnet configs: %s", err) + } + subnetConfigContent = base64.StdEncoding.EncodeToString(marshaledConfigs) + } + + // Build the base64-encoded chain config to enable debug logging on the L1 + chainConfigMap := map[string]json.RawMessage{ + result.ChainID.String(): json.RawMessage(`{"log-level":"debug"}`), + } + marshaledChainConfigs, err := json.Marshal(chainConfigMap) + if err != nil { + log.Fatalf("failed to marshal chain configs: %s", err) + } + chainConfigContent := base64.StdEncoding.EncodeToString(marshaledChainConfigs) + + for _, node := range network.Nodes { + node.Flags[config.TrackSubnetsKey] = result.SubnetID.String() + if subnetConfigContent != "" { + node.Flags[config.SubnetConfigContentKey] = subnetConfigContent + } + node.Flags[config.ChainConfigContentKey] = chainConfigContent + node.Flags[config.LogLevelKey] = "debug" + node.Flags[config.LogDisplayLevelKey] = "debug" + if err := node.Write(); err != nil { + log.Fatalf("failed to write config for %s: %s", node.NodeID, err) + } + log.Printf(" configured %s", node.NodeID) + } + + log.Println("restarting network...") + if err := network.Restart(ctx); err != nil { + log.Fatalf("failed to restart network: %s", err) + } + log.Println("network restarted and healthy") +} + +func printResult(result l1Result) { + fmt.Println("\n=== L1 Created Successfully ===") + fmt.Printf("Subnet ID: %s\n", result.SubnetID) + fmt.Printf("Chain ID: %s\n", result.ChainID) + fmt.Printf("Conversion TX: %s\n", result.ConvertTxID) + fmt.Printf("Conversion ID: %s\n", result.ConversionID) + fmt.Printf("Validators: %d nodes\n", len(result.Validators)) + fmt.Printf("VM: subnet-evm (%s)\n", constants.SubnetEVMID) + fmt.Printf("\nSUBNET_ID=%s\n", result.SubnetID) +} + +// writeChainsJSON writes the P-Chain, C-Chain, and L1 chain metadata +// to a JSON file for the frontend to consume. +func writeChainsJSON(outputPath string, result l1Result) { + type chainEntry struct { + Name string `json:"name"` + ChainID string `json:"chainId"` + RPCPath string `json:"rpcPath"` + SubnetID string `json:"subnetId,omitempty"` + VM string `json:"vm"` + Consensus string `json:"consensus,omitempty"` + ConversionTx string `json:"conversionTx,omitempty"` + ConversionID string `json:"conversionId,omitempty"` + ValidatorCount int `json:"validatorCount,omitempty"` + } + + chains := []chainEntry{ + { + Name: "P-Chain", + ChainID: "P", + RPCPath: "/ext/bc/P", + VM: "platformvm", + Consensus: "snowman", + }, + { + Name: "C-Chain", + ChainID: "C", + RPCPath: "/ext/bc/C/rpc", + VM: "evm", + Consensus: "snowman", + }, + { + Name: result.ChainName, + ChainID: result.ChainID.String(), + RPCPath: fmt.Sprintf("/ext/bc/%s/rpc", result.ChainID), + SubnetID: result.SubnetID.String(), + VM: "subnet-evm", + Consensus: result.ConsensusType, + ConversionTx: result.ConvertTxID.String(), + ConversionID: result.ConversionID.String(), + ValidatorCount: len(result.Validators), + }, + } + + if err := os.MkdirAll(filepath.Dir(outputPath), 0o755); err != nil { + log.Fatalf("failed to create output dir: %s", err) + } + data, err := json.MarshalIndent(chains, "", " ") + if err != nil { + log.Fatalf("failed to marshal chains: %s", err) + } + if err := os.WriteFile(outputPath, data, 0o644); err != nil { + log.Fatalf("failed to write chains.json: %s", err) + } + fmt.Printf("\nWrote chains to %s\n", outputPath) +} diff --git a/scripts/simplex/fund_nodes.sh b/scripts/simplex/fund_nodes.sh new file mode 100755 index 000000000000..550420a404c8 --- /dev/null +++ b/scripts/simplex/fund_nodes.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +NETWORK_DIR="${HOME}/.tmpnet/networks/latest" +if [ -f "${NETWORK_DIR}/network.env" ]; then + source "${NETWORK_DIR}/network.env" + NETWORK_DIR="${TMPNET_NETWORK_DIR:-${NETWORK_DIR}}" +fi +OUTPUT="${SCRIPT_DIR}/tx-frontend/public/nodes.json" +CHAINS_FILE="${SCRIPT_DIR}/tx-frontend/public/chains.json" +FUND_AMOUNT="100" # AVAX per node + +# Sync node URIs from the network directory +echo "Syncing node URIs from ${NETWORK_DIR}..." +uris=() +for node_dir in "${NETWORK_DIR}"/NodeID-*; do + [ -d "$node_dir" ] || continue + process_file="${node_dir}/process.json" + [ -f "$process_file" ] || continue + uri=$(python3 -c "import json; print(json.load(open('${process_file}'))['uri'])" 2>/dev/null || true) + [ -n "$uri" ] && uris+=("\"${uri}\"") +done +printf '[%s]\n' "$(IFS=,; echo "${uris[*]}")" > "$OUTPUT" +echo "Wrote ${#uris[@]} node URIs to ${OUTPUT}" + +# Build the list of EVM RPC endpoints to fund on +# Always include C-Chain, plus any EVM chains from chains.json +FIRST_URI=$(python3 -c "import json; entries=json.load(open('${OUTPUT}')); uri=entries[0] if isinstance(entries[0],str) else entries[0]['uri']; print(uri)") + +RPC_URLS="${FIRST_URI}/ext/bc/C/rpc" + +if [ -f "$CHAINS_FILE" ]; then + # Add all EVM-compatible chains (not platformvm) + EXTRA_RPCS=$(python3 -c " +import json +chains = json.load(open('${CHAINS_FILE}')) +for c in chains: + if c.get('vm') not in ('platformvm',) and c.get('chainId') != 'C': + print(c['rpcPath']) +") + for rpc_path in $EXTRA_RPCS; do + RPC_URLS="${RPC_URLS}|${FIRST_URI}${rpc_path}" + done +fi + +echo "Funding $FUND_AMOUNT AVAX per node on all EVM chains..." + +export OUTPUT FUND_AMOUNT RPC_URLS +NODE_DATA=$(python3 << 'PYEOF' +import json, hashlib, os + +EWOQ_KEY = "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027" + +output = os.environ["OUTPUT"] +fund_amount = os.environ["FUND_AMOUNT"] +rpc_urls = os.environ["RPC_URLS"].split("|") + +with open(output) as f: + entries = json.load(f) + +nodes = [] +for i, entry in enumerate(entries): + uri = entry if isinstance(entry, str) else entry.get("uri", "") + seed = f"simplex-node-{i}".encode() + priv = hashlib.sha256(seed).hexdigest() + nodes.append({"uri": uri, "privateKey": priv, "index": i}) + +print(json.dumps({"nodes": nodes, "ewoqKey": EWOQ_KEY, "rpcUrls": rpc_urls, "fundAmount": fund_amount})) +PYEOF +) + +FUND_SCRIPT="${SCRIPT_DIR}/tx-frontend/fund.mjs" + +cat > "$FUND_SCRIPT" << 'JSEOF' +import { JsonRpcProvider, Wallet, parseEther, formatEther } from "ethers"; +import { writeFileSync } from "fs"; + +const config = JSON.parse(process.argv[2]); +const { nodes, ewoqKey, rpcUrls, fundAmount } = config; + +const results = []; + +for (const rpcUrl of rpcUrls) { + console.log(`\n=== Funding on ${rpcUrl} ===`); + + const provider = new JsonRpcProvider(rpcUrl); + const ewoqWallet = new Wallet(ewoqKey, provider); + + try { + const ewoqBal = await provider.getBalance(ewoqWallet.address); + console.log(`Ewoq balance: ${formatEther(ewoqBal)} AVAX`); + } catch (e) { + console.log(`Skipping ${rpcUrl}: ${e.message}`); + continue; + } + + let nonce = await provider.getTransactionCount(ewoqWallet.address); + + for (const node of nodes) { + const wallet = new Wallet(node.privateKey, provider); + const address = wallet.address; + + const currentBal = await provider.getBalance(address); + const currentBalEth = parseFloat(formatEther(currentBal)); + + if (currentBalEth < parseFloat(fundAmount) * 0.9) { + const needed = parseFloat(fundAmount) - currentBalEth; + console.log(` Node ${node.index}: ${address} — funding ${needed.toFixed(2)} AVAX...`); + const tx = await ewoqWallet.sendTransaction({ + to: address, + value: parseEther(needed.toFixed(18)), + nonce: nonce++, + }); + await tx.wait(); + console.log(` tx: ${tx.hash}`); + } else { + console.log(` Node ${node.index}: ${address} — already funded (${currentBalEth.toFixed(2)} AVAX)`); + } + + // Only build results from the first RPC (C-Chain) + if (rpcUrl === rpcUrls[0]) { + results.push({ uri: node.uri, address }); + } + } +} + +const outputPath = process.argv[3]; +writeFileSync(outputPath, JSON.stringify(results, null, 2)); +console.log(`\nWrote ${results.length} nodes with addresses to ${outputPath}`); +JSEOF + +cd "${SCRIPT_DIR}/tx-frontend" +node fund.mjs "$NODE_DATA" "$OUTPUT" +rm -f "$FUND_SCRIPT" + +echo "Done!" diff --git a/scripts/simplex/run.sh b/scripts/simplex/run.sh new file mode 100755 index 000000000000..63dfb090fbd2 --- /dev/null +++ b/scripts/simplex/run.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +AVALANCHE_PATH=$(cd "${SCRIPT_DIR}/../.." && pwd) + +cd "${AVALANCHE_PATH}" + +./scripts/simplex/clean.sh +./scripts/build.sh +./scripts/run_tmpnetctl.sh start-network --node-count=5 --avalanchego-path=./build/avalanchego +./scripts/simplex/create_l1.sh +./scripts/simplex/fund_nodes.sh diff --git a/scripts/simplex/send_tx/BUILD.bazel b/scripts/simplex/send_tx/BUILD.bazel new file mode 100644 index 000000000000..0f817a81561f --- /dev/null +++ b/scripts/simplex/send_tx/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "send_tx_lib", + srcs = ["main.go"], + importpath = "github.com/ava-labs/avalanchego/scripts/simplex/send_tx", + visibility = ["//visibility:private"], + deps = [ + "@com_github_ava_labs_libevm//common", + "@com_github_ava_labs_libevm//core/types", + "@com_github_ava_labs_libevm//crypto", + "@com_github_ava_labs_libevm//ethclient", + ], +) + +go_binary( + name = "send_tx", + embed = [":send_tx_lib"], + visibility = ["//visibility:public"], +) diff --git a/scripts/simplex/send_tx/main.go b/scripts/simplex/send_tx/main.go new file mode 100644 index 000000000000..34aab6588e6e --- /dev/null +++ b/scripts/simplex/send_tx/main.go @@ -0,0 +1,181 @@ +package main + +import ( + "context" + "crypto/ecdsa" + "encoding/json" + "flag" + "fmt" + "log" + "math/big" + "os" + "time" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/crypto" + "github.com/ava-labs/libevm/ethclient" +) + +func main() { + nodesFile := flag.String("nodes", "", "Path to nodes.json") + chainsFile := flag.String("chains", "", "Path to chains.json") + flag.Parse() + + if *nodesFile == "" || *chainsFile == "" { + log.Fatal("--nodes and --chains are required") + } + + // Read node URIs + nodesData, err := os.ReadFile(*nodesFile) + if err != nil { + log.Fatalf("failed to read nodes file: %s", err) + } + type nodeEntry struct { + URI string `json:"uri"` + Address string `json:"address"` + } + var nodes []nodeEntry + if err := json.Unmarshal(nodesData, &nodes); err != nil { + log.Fatalf("failed to parse nodes file: %s", err) + } + if len(nodes) == 0 { + log.Fatal("no nodes found") + } + nodeURIs := make([]string, len(nodes)) + for i, n := range nodes { + nodeURIs[i] = n.URI + } + + // Read chains to find the simplex L1 chain RPC path + chainsData, err := os.ReadFile(*chainsFile) + if err != nil { + log.Fatalf("failed to read chains file: %s", err) + } + type chainEntry struct { + Name string `json:"name"` + ChainID string `json:"chainId"` + RPCPath string `json:"rpcPath"` + VM string `json:"vm"` + } + var chains []chainEntry + if err := json.Unmarshal(chainsData, &chains); err != nil { + log.Fatalf("failed to parse chains file: %s", err) + } + + var rpcURL string + for _, c := range chains { + if c.VM == "subnet-evm" { + rpcURL = nodeURIs[0] + c.RPCPath + break + } + } + if rpcURL == "" { + log.Fatal("no subnet-evm chain found in chains.json") + } + + log.Printf("RPC URL: %s", rpcURL) + + // Ewoq private key (pre-funded in genesis) + ewoqKeyHex := "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027" + privateKey, err := crypto.HexToECDSA(ewoqKeyHex) + if err != nil { + log.Fatalf("failed to parse private key: %s", err) + } + fromAddress := crypto.PubkeyToAddress(privateKey.PublicKey) + log.Printf("From address: %s", fromAddress.Hex()) + + // Connect to the RPC endpoint + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + client, err := ethclient.DialContext(ctx, rpcURL) + if err != nil { + log.Fatalf("failed to connect to RPC: %s", err) + } + defer client.Close() + + // Check chain ID + chainID, err := client.ChainID(ctx) + if err != nil { + log.Fatalf("failed to get chain ID: %s", err) + } + log.Printf("Chain ID: %s", chainID) + + // Check balance + balance, err := client.BalanceAt(ctx, fromAddress, nil) + if err != nil { + log.Fatalf("failed to get balance: %s", err) + } + log.Printf("Balance: %s wei", balance) + + // Check current block number + blockNum, err := client.BlockNumber(ctx) + if err != nil { + log.Fatalf("failed to get block number: %s", err) + } + log.Printf("Current block number: %d", blockNum) + + // Get nonce + nonce, err := client.PendingNonceAt(ctx, fromAddress) + if err != nil { + log.Fatalf("failed to get nonce: %s", err) + } + log.Printf("Nonce: %d", nonce) + + // Get gas price + gasPrice, err := client.SuggestGasPrice(ctx) + if err != nil { + log.Fatalf("failed to get gas price: %s", err) + } + log.Printf("Gas price: %s", gasPrice) + + // Send a simple self-transfer of 0 AVAX to trigger a pending tx event + toAddress := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678") + value := big.NewInt(1e18) // 1 AVAX + + tx := types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &toAddress, + Value: value, + Gas: 21000, + GasPrice: gasPrice, + Data: nil, + }) + + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) + if err != nil { + log.Fatalf("failed to sign tx: %s", err) + } + log.Printf("Signed tx hash: %s", signedTx.Hash().Hex()) + + // Send the transaction + log.Println("Sending transaction...") + err = client.SendTransaction(ctx, signedTx) + if err != nil { + log.Fatalf("failed to send transaction: %s", err) + } + log.Printf("Transaction sent! Hash: %s", signedTx.Hash().Hex()) + + // Wait for the receipt + log.Println("Waiting for receipt...") + for i := 0; i < 30; i++ { + receipt, err := client.TransactionReceipt(ctx, signedTx.Hash()) + if err == nil { + log.Printf("Transaction mined in block %d, status: %d", receipt.BlockNumber, receipt.Status) + // Check new block number + newBlockNum, _ := client.BlockNumber(ctx) + log.Printf("New block number: %d", newBlockNum) + return + } + fmt.Print(".") + time.Sleep(1 * time.Second) + } + log.Println("\nTransaction not mined within 30 seconds") + + // Check pending nonce to see if tx is in the pool + pendingNonce, _ := client.PendingNonceAt(ctx, fromAddress) + log.Printf("Pending nonce: %d (was %d)", pendingNonce, nonce) + + _ = privateKey.Public().(*ecdsa.PublicKey) +} diff --git a/scripts/simplex/stop_network.sh b/scripts/simplex/stop_network.sh new file mode 100755 index 000000000000..4b106fa3eafe --- /dev/null +++ b/scripts/simplex/stop_network.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -euo pipefail + +AVALANCHE_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) +cd "${AVALANCHE_PATH}" + +echo "Stopping local network..." +./scripts/run_tmpnetctl.sh stop-network diff --git a/scripts/simplex/tx-frontend/.gitignore b/scripts/simplex/tx-frontend/.gitignore new file mode 100644 index 000000000000..5ef6a5207802 --- /dev/null +++ b/scripts/simplex/tx-frontend/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/scripts/simplex/tx-frontend/AGENTS.md b/scripts/simplex/tx-frontend/AGENTS.md new file mode 100644 index 000000000000..8bd0e39085d5 --- /dev/null +++ b/scripts/simplex/tx-frontend/AGENTS.md @@ -0,0 +1,5 @@ + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + diff --git a/scripts/simplex/tx-frontend/CLAUDE.md b/scripts/simplex/tx-frontend/CLAUDE.md new file mode 100644 index 000000000000..43c994c2d361 --- /dev/null +++ b/scripts/simplex/tx-frontend/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/scripts/simplex/tx-frontend/README.md b/scripts/simplex/tx-frontend/README.md new file mode 100644 index 000000000000..e215bc4ccf13 --- /dev/null +++ b/scripts/simplex/tx-frontend/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/scripts/simplex/tx-frontend/app/favicon.ico b/scripts/simplex/tx-frontend/app/favicon.ico new file mode 100644 index 000000000000..718d6fea4835 Binary files /dev/null and b/scripts/simplex/tx-frontend/app/favicon.ico differ diff --git a/scripts/simplex/tx-frontend/app/globals.css b/scripts/simplex/tx-frontend/app/globals.css new file mode 100644 index 000000000000..a2dc41ecee5e --- /dev/null +++ b/scripts/simplex/tx-frontend/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/scripts/simplex/tx-frontend/app/layout.tsx b/scripts/simplex/tx-frontend/app/layout.tsx new file mode 100644 index 000000000000..ff65235f41de --- /dev/null +++ b/scripts/simplex/tx-frontend/app/layout.tsx @@ -0,0 +1,33 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Simplex TX Dashboard", + description: "Transaction dashboard for local Avalanche network", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/scripts/simplex/tx-frontend/app/page.tsx b/scripts/simplex/tx-frontend/app/page.tsx new file mode 100644 index 000000000000..dd427271c5d7 --- /dev/null +++ b/scripts/simplex/tx-frontend/app/page.tsx @@ -0,0 +1,718 @@ +"use client"; + +import { useState, useCallback, useEffect, useRef } from "react"; +import { + JsonRpcProvider, + Wallet, + parseEther, + formatEther, + getAddress, + sha256, + toUtf8Bytes, + type TransactionResponse, +} from "ethers"; + +const EWOQ_PRIVATE_KEY = + "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027"; +const POLL_INTERVAL = 2000; + +function nodePrivateKey(index: number): string { + return sha256(toUtf8Bytes(`simplex-node-${index}`)); +} + +interface NodeInfo { + uri: string; + address: string | null; + nodeID: string | null; + healthy: boolean; + blockHeight: number | null; + balance: string | null; +} + +interface TxRecord { + hash: string; + from: string; + to: string; + amount: string; + status: "pending" | "confirmed" | "failed"; + blockNumber?: number; +} + +interface ChainInfo { + name: string; + chainId: string; + rpcPath: string; + subnetId?: string; + vm: string; + consensus?: string; + conversionTx?: string; + conversionId?: string; + validatorCount?: number; +} + +async function rpcCall(url: string, method: string, params?: Record) { + const res = await fetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params: params ?? {} }), + }); + return res.json(); +} + +interface PChainTx { + type: string; + txID: string; + blockHeight: number; + timestamp?: string; +} + +function inferPChainTxType(unsignedTx: Record): string { + if ("validators" in unsignedTx && "chainID" in unsignedTx && "address" in unsignedTx) return "ConvertSubnetToL1"; + if ("chainName" in unsignedTx) return "CreateChain"; + if ("owner" in unsignedTx && !("validator" in unsignedTx)) return "CreateSubnet"; + if ("validator" in unsignedTx && "subnetID" in unsignedTx) return "AddSubnetValidator"; + if ("message" in unsignedTx && "balance" in unsignedTx) return "RegisterL1Validator"; + if ("message" in unsignedTx) return "SetL1ValidatorWeight"; + if ("validationID" in unsignedTx && "balance" in unsignedTx) return "IncreaseL1ValidatorBalance"; + if ("validationID" in unsignedTx) return "DisableL1Validator"; + if ("stake" in unsignedTx) return "AddValidator"; + return "Unknown"; +} + +async function fetchPChainBlocks( + baseUri: string, + fromHeight: number, + toHeight: number +): Promise { + const txs: PChainTx[] = []; + for (let h = fromHeight; h <= toHeight; h++) { + try { + const res = await rpcCall(`${baseUri}/ext/bc/P`, "platform.getBlockByHeight", { + height: String(h), + encoding: "json", + }); + const block = res?.result?.block; + if (!block) continue; + + const blockTxs = block.txs ?? []; + for (const tx of blockTxs) { + const txID = tx?.id ?? ""; + const unsignedTx = (tx?.unsignedTx ?? {}) as Record; + txs.push({ + type: inferPChainTxType(unsignedTx), + txID, + blockHeight: h, + timestamp: block.time ? String(block.time) : undefined, + }); + } + } catch { + // skip block + } + } + return txs; +} + +async function fetchNodeInfo( + uri: string, + address: string | null, + chainRpcPath?: string +): Promise { + const base: NodeInfo = { + uri, + address, + nodeID: null, + healthy: false, + blockHeight: null, + balance: null, + }; + try { + const [idRes, healthRes] = await Promise.all([ + rpcCall(`${uri}/ext/info`, "info.getNodeID"), + rpcCall(`${uri}/ext/health`, "health.health"), + ]); + base.nodeID = idRes?.result?.nodeID ?? null; + base.healthy = healthRes?.result?.healthy ?? false; + + const rpcPath = chainRpcPath || "/ext/bc/C/rpc"; + const provider = new JsonRpcProvider(`${uri}${rpcPath}`); + base.blockHeight = await provider.getBlockNumber(); + + if (address) { + const bal = await provider.getBalance(address); + base.balance = formatEther(bal); + } + } catch { + // node unreachable + } + return base; +} + +export default function Home() { + const [chains, setChains] = useState([]); + const [selectedChain, setSelectedChain] = useState(null); + const [nodeEntries, setNodeEntries] = useState< + { uri: string; address: string | null }[] + >([]); + const [nodes, setNodes] = useState([]); + const [connected, setConnected] = useState(false); + const [activeNode, setActiveNode] = useState(""); + const [shouldAutoConnect, setShouldAutoConnect] = useState(false); + const fromNodeIndex = nodes.findIndex((n) => n.uri === activeNode); + + const [to, setTo] = useState(""); + const [amount, setAmount] = useState("1"); + const [sentTxs, setSentTxs] = useState([]); + const [chainTxs, setChainTxs] = useState([]); + const [pChainTxs, setPChainTxs] = useState([]); + const [sending, setSending] = useState(false); + const [error, setError] = useState(""); + const [balance, setBalance] = useState(null); + const [senderAddress, setSenderAddress] = useState(null); + const [chainBlockHeight, setChainBlockHeight] = useState(null); + const pollingRef = useRef | null>(null); + const lastScannedBlock = useRef(0); + const lastPChainBlock = useRef(0); + + // Auto-load node entries and chains + useEffect(() => { + fetch("/nodes.json") + .then((res) => res.json()) + .then((entries: ({ uri: string; address: string } | string)[]) => { + if (entries.length > 0) { + const parsed = entries.map((e) => + typeof e === "string" ? { uri: e, address: null } : e + ); + setNodeEntries(parsed); + setShouldAutoConnect(true); + } + }) + .catch(() => {}); + + fetch("/chains.json") + .then((res) => res.json()) + .then((data: ChainInfo[]) => { + setChains(data); + // Default to C-Chain + const cChain = data.find((c) => c.chainId === "C"); + if (cChain) setSelectedChain(cChain); + }) + .catch(() => {}); + }, []); + + // Reset tx history and block scanner when chain changes + useEffect(() => { + setChainTxs([]); + setSentTxs([]); + setPChainTxs([]); + setChainBlockHeight(null); + lastScannedBlock.current = 0; + lastPChainBlock.current = 0; + }, [selectedChain]); + + const getChainRpcUrl = useCallback(() => { + if (!activeNode || !selectedChain) return null; + return `${activeNode}${selectedChain.rpcPath}`; + }, [activeNode, selectedChain]); + + const connect = useCallback(async () => { + if (nodeEntries.length === 0) return; + + setError(""); + try { + const infos = await Promise.all( + nodeEntries.map((e) => fetchNodeInfo(e.uri, e.address)) + ); + setNodes(infos); + + const firstHealthy = infos.find((n) => n.healthy); + const primary = firstHealthy?.uri ?? nodeEntries[0].uri; + setActiveNode(primary); + + const provider = new JsonRpcProvider(`${primary}/ext/bc/C/rpc`); + const wallet = new Wallet(EWOQ_PRIVATE_KEY, provider); + setSenderAddress(wallet.address); + const bal = await provider.getBalance(wallet.address); + setBalance(formatEther(bal)); + setConnected(true); + } catch (e: unknown) { + setError(`Failed to connect: ${e instanceof Error ? e.message : e}`); + setConnected(false); + } + }, [nodeEntries]); + + useEffect(() => { + if (shouldAutoConnect && !connected) { + connect(); + setShouldAutoConnect(false); + } + }, [shouldAutoConnect, connected, connect]); + + const isEvmChain = + selectedChain && selectedChain.vm !== "platformvm"; + + const pollNetwork = useCallback(async () => { + if (!activeNode) return; + try { + // Refresh node statuses with balances from the selected chain + const chainRpcPath = selectedChain && selectedChain.vm !== "platformvm" + ? selectedChain.rpcPath + : "/ext/bc/C/rpc"; + const infos = await Promise.all( + nodeEntries.map((e) => fetchNodeInfo(e.uri, e.address, chainRpcPath)) + ); + setNodes(infos); + + // Sender balance from selected chain (or C-chain for non-EVM) + const provider = new JsonRpcProvider(`${activeNode}${chainRpcPath}`); + const wallet = new Wallet(EWOQ_PRIVATE_KEY, provider); + const bal = await provider.getBalance(wallet.address); + setBalance(formatEther(bal)); + + // Poll selected chain for txs (only EVM chains) + const rpcUrl = getChainRpcUrl(); + if (rpcUrl && isEvmChain) { + const provider = new JsonRpcProvider(rpcUrl); + const height = await provider.getBlockNumber(); + setChainBlockHeight(height); + + const startBlock = Math.max( + lastScannedBlock.current + 1, + Math.max(0, height - 20) + ); + const newTxs: TxRecord[] = []; + for (let i = startBlock; i <= height; i++) { + const block = await provider.getBlock(i, true); + if (!block) continue; + const txs: TransactionResponse[] = + block.prefetchedTransactions ?? []; + for (const tx of txs) { + newTxs.push({ + hash: tx.hash, + from: tx.from, + to: tx.to ?? "Contract Creation", + amount: formatEther(tx.value), + status: "confirmed", + blockNumber: i, + }); + } + } + if (newTxs.length > 0) { + setChainTxs((prev) => [...newTxs, ...prev].slice(0, 50)); + } + lastScannedBlock.current = height; + } + + // Poll P-Chain if selected + if (selectedChain?.vm === "platformvm") { + const heightRes = await rpcCall(`${activeNode}/ext/bc/P`, "platform.getHeight"); + const pHeight = Number(heightRes?.result?.height ?? 0); + setChainBlockHeight(pHeight); + + const startH = Math.max( + lastPChainBlock.current + 1, + Math.max(0, pHeight - 20) + ); + if (startH <= pHeight) { + const newPTxs = await fetchPChainBlocks(activeNode, startH, pHeight); + if (newPTxs.length > 0) { + setPChainTxs((prev) => [...newPTxs, ...prev].slice(0, 50)); + } + lastPChainBlock.current = pHeight; + } + } + } catch { + // silently fail polls + } + }, [activeNode, nodeEntries, getChainRpcUrl, isEvmChain, selectedChain]); + + useEffect(() => { + if (!connected) return; + pollingRef.current = setInterval(pollNetwork, POLL_INTERVAL); + return () => { + if (pollingRef.current) clearInterval(pollingRef.current); + }; + }, [connected, pollNetwork]); + + const sendTx = async () => { + const rpcUrl = getChainRpcUrl(); + if (!rpcUrl || !to || !amount || fromNodeIndex < 0) return; + setSending(true); + setError(""); + try { + const provider = new JsonRpcProvider(rpcUrl); + const privKey = nodePrivateKey(fromNodeIndex); + const wallet = new Wallet(privKey, provider); + const tx = await wallet.sendTransaction({ + to: getAddress(to), + value: parseEther(amount), + }); + const record: TxRecord = { + hash: tx.hash, + from: wallet.address, + to, + amount, + status: "pending", + }; + setSentTxs((prev) => [record, ...prev]); + const receipt = await tx.wait(); + setSentTxs((prev) => + prev.map((t) => + t.hash === tx.hash + ? { + ...t, + status: receipt?.status === 1 ? "confirmed" : "failed", + blockNumber: receipt?.blockNumber, + } + : t + ) + ); + } catch (e: unknown) { + setError(`TX failed: ${e instanceof Error ? e.message : e}`); + } finally { + setSending(false); + } + }; + + const mono = "font-[family-name:var(--font-geist-mono)]"; + const card = + "p-4 bg-zinc-100 dark:bg-zinc-900 rounded-lg border border-zinc-200 dark:border-zinc-800"; + const inputCls = + "w-full bg-white dark:bg-zinc-800 border border-zinc-300 dark:border-zinc-700 rounded px-3 py-2 text-sm"; + + return ( +
+ {/* Nodes Overview */} + {nodes.length > 0 && ( +
+
+ {nodes.map((node, i) => ( +
node.healthy && setActiveNode(node.uri)} + className={`flex-shrink-0 w-48 p-4 rounded-lg border cursor-pointer transition-colors ${ + node.uri === activeNode + ? "bg-blue-50 dark:bg-blue-950 border-blue-300 dark:border-blue-800" + : "bg-zinc-100 dark:bg-zinc-900 border-zinc-200 dark:border-zinc-800 hover:border-zinc-400 dark:hover:border-zinc-600" + }`} + > +
+ Node {i + 1} + +
+
+ {node.nodeID ? `${node.nodeID.slice(0, 18)}...` : "unknown"} +
+
+ {node.uri.replace("http://127.0.0.1:", ":")} +
+ {node.address && ( +
+ {node.address.slice(0, 8)}...{node.address.slice(-6)} +
+ )} + {node.blockHeight !== null && ( +
+ Block{" "} + + {node.blockHeight} + +
+ )} + {node.balance !== null && ( +
+ + {parseFloat(node.balance).toFixed(2)} AVAX + +
+ )} + {node.uri === activeNode && ( +
+ active +
+ )} +
+ ))} +
+
+ )} + + {/* Chain pills */} + {chains.length > 0 && ( +
+ + Chains: + + {chains.map((c) => ( + + ))} +
+ )} + + {/* Selected chain info */} + {selectedChain && (selectedChain.subnetId || selectedChain.conversionTx) && ( +
+
+ {selectedChain.subnetId && ( + <> + Subnet ID + {selectedChain.subnetId} + + )} + Chain ID + {selectedChain.chainId} + {selectedChain.conversionTx && ( + <> + Conversion TX + {selectedChain.conversionTx} + + )} + {selectedChain.conversionId && ( + <> + Conversion ID + {selectedChain.conversionId} + + )} + {selectedChain.validatorCount && ( + <> + Validators + {selectedChain.validatorCount} nodes + + )} + VM + {selectedChain.vm} + {selectedChain.consensus && ( + <> + Consensus + {selectedChain.consensus} + + )} + {chainBlockHeight !== null && ( + <> + Block Height + {chainBlockHeight} + + )} +
+
+ )} + + {/* Send TX - only for EVM chains */} + {isEvmChain && ( +
+

Send Transaction

+
+
+ +
+ {fromNodeIndex >= 0 ? ( + + Node {fromNodeIndex + 1} + {nodes[fromNodeIndex]?.address && ( + + {nodes[fromNodeIndex].address!.slice(0, 10)}...{nodes[fromNodeIndex].address!.slice(-6)} + + )} + + ) : ( + Select an active node above + )} +
+
+
+ +
+ {nodes + .filter((n) => n.address) + .map((node, i) => ( + + ))} +
+ setTo(e.target.value)} + /> +
+
+ + setAmount(e.target.value)} + /> +
+ +
+
+ )} + + {error && ( +
+ {error} +
+ )} + + {/* Sent TX History */} + {sentTxs.length > 0 && ( +
+

Sent Transactions

+
+ {sentTxs.map((tx) => ( + + ))} +
+
+ )} + + {/* On-Chain TX History (EVM chains) */} + {chainTxs.length > 0 && isEvmChain && ( +
+

+ On-Chain Transactions + {selectedChain && ( + + ({selectedChain.name}) + + )} +

+
+ {[...chainTxs] + .sort((a, b) => (b.blockNumber ?? 0) - (a.blockNumber ?? 0)) + .map((tx) => ( + + ))} +
+
+ )} + + {/* P-Chain TX History */} + {selectedChain?.vm === "platformvm" && pChainTxs.length > 0 && ( +
+

+ P-Chain Transactions +

+
+ {[...pChainTxs] + .sort((a, b) => b.blockHeight - a.blockHeight) + .map((tx, i) => ( +
+
+ + {tx.txID ? `${tx.txID.slice(0, 18)}...${tx.txID.slice(-8)}` : "—"} + + + {tx.type} + +
+
+ Block #{tx.blockHeight} +
+
+ ))} +
+
+ )} +
+ ); +} + +function TxRow({ tx, mono }: { tx: TxRecord; mono: string }) { + return ( +
+
+ + {tx.hash.slice(0, 18)}...{tx.hash.slice(-8)} + + + {tx.status} + +
+
+ {tx.from.slice(0, 10)}... + {" → "} + + {tx.to.length > 20 ? `${tx.to.slice(0, 10)}...` : tx.to} + + + {parseFloat(tx.amount).toFixed(2)} AVAX + +
+ {tx.blockNumber && ( +
Block #{tx.blockNumber}
+ )} +
+ ); +} diff --git a/scripts/simplex/tx-frontend/eslint.config.mjs b/scripts/simplex/tx-frontend/eslint.config.mjs new file mode 100644 index 000000000000..05e726d1b420 --- /dev/null +++ b/scripts/simplex/tx-frontend/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/scripts/simplex/tx-frontend/fund.mjs b/scripts/simplex/tx-frontend/fund.mjs new file mode 100644 index 000000000000..ee80bc956330 --- /dev/null +++ b/scripts/simplex/tx-frontend/fund.mjs @@ -0,0 +1,55 @@ +import { JsonRpcProvider, Wallet, parseEther, formatEther } from "ethers"; +import { writeFileSync } from "fs"; + +const config = JSON.parse(process.argv[2]); +const { nodes, ewoqKey, rpcUrls, fundAmount } = config; + +const results = []; + +for (const rpcUrl of rpcUrls) { + console.log(`\n=== Funding on ${rpcUrl} ===`); + + const provider = new JsonRpcProvider(rpcUrl); + const ewoqWallet = new Wallet(ewoqKey, provider); + + try { + const ewoqBal = await provider.getBalance(ewoqWallet.address); + console.log(`Ewoq balance: ${formatEther(ewoqBal)} AVAX`); + } catch (e) { + console.log(`Skipping ${rpcUrl}: ${e.message}`); + continue; + } + + let nonce = await provider.getTransactionCount(ewoqWallet.address); + + for (const node of nodes) { + const wallet = new Wallet(node.privateKey, provider); + const address = wallet.address; + + const currentBal = await provider.getBalance(address); + const currentBalEth = parseFloat(formatEther(currentBal)); + + if (currentBalEth < parseFloat(fundAmount) * 0.9) { + const needed = parseFloat(fundAmount) - currentBalEth; + console.log(` Node ${node.index}: ${address} — funding ${needed.toFixed(2)} AVAX...`); + const tx = await ewoqWallet.sendTransaction({ + to: address, + value: parseEther(needed.toFixed(18)), + nonce: nonce++, + }); + await tx.wait(); + console.log(` tx: ${tx.hash}`); + } else { + console.log(` Node ${node.index}: ${address} — already funded (${currentBalEth.toFixed(2)} AVAX)`); + } + + // Only build results from the first RPC (C-Chain) + if (rpcUrl === rpcUrls[0]) { + results.push({ uri: node.uri, address }); + } + } +} + +const outputPath = process.argv[3]; +writeFileSync(outputPath, JSON.stringify(results, null, 2)); +console.log(`\nWrote ${results.length} nodes with addresses to ${outputPath}`); diff --git a/scripts/simplex/tx-frontend/next.config.ts b/scripts/simplex/tx-frontend/next.config.ts new file mode 100644 index 000000000000..e9ffa3083ad2 --- /dev/null +++ b/scripts/simplex/tx-frontend/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/scripts/simplex/tx-frontend/package-lock.json b/scripts/simplex/tx-frontend/package-lock.json new file mode 100644 index 000000000000..67bdfeb7dc32 --- /dev/null +++ b/scripts/simplex/tx-frontend/package-lock.json @@ -0,0 +1,6679 @@ +{ + "name": "tx-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tx-frontend", + "version": "0.1.0", + "dependencies": { + "ethers": "^6.16.0", + "next": "16.2.3", + "react": "19.2.4", + "react-dom": "19.2.4" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.3", + "tailwindcss": "^4", + "typescript": "^5" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next/env": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.3.tgz", + "integrity": "sha512-ZWXyj4uNu4GCWQw9cjRxWlbD+33mcDszIo9iQxFnBX3Wmgq9ulaSJcl6VhuWx5pCWqqD+9W6Wfz7N0lM5lYPMA==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.3.tgz", + "integrity": "sha512-nE/b9mht28XJxjTwKs/yk7w4XTaU3t40UHVAky6cjiijdP/SEy3hGsnQMPxmXPTpC7W4/97okm6fngKnvCqVaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.3.tgz", + "integrity": "sha512-u37KDKTKQ+OQLvY+z7SNXixwo4Q2/IAJFDzU1fYe66IbCE51aDSAzkNDkWmLN0yjTUh4BKBd+hb69jYn6qqqSg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.3.tgz", + "integrity": "sha512-gHjL/qy6Q6CG3176FWbAKyKh9IfntKZTB3RY/YOJdDFpHGsUDXVH38U4mMNpHVGXmeYW4wj22dMp1lTfmu/bTQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.3.tgz", + "integrity": "sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.3.tgz", + "integrity": "sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.3.tgz", + "integrity": "sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.3.tgz", + "integrity": "sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.3.tgz", + "integrity": "sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.3.tgz", + "integrity": "sha512-Ibm29/GgB/ab5n7XKqlStkm54qqZE8v2FnijUPBgrd67FWrac45o/RsNlaOWjme/B5UqeWt/8KM4aWBwA1D2Kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", + "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "postcss": "^8.5.6", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", + "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.1.tgz", + "integrity": "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.1", + "@typescript-eslint/type-utils": "8.58.1", + "@typescript-eslint/utils": "8.58.1", + "@typescript-eslint/visitor-keys": "8.58.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.1.tgz", + "integrity": "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.1", + "@typescript-eslint/types": "8.58.1", + "@typescript-eslint/typescript-estree": "8.58.1", + "@typescript-eslint/visitor-keys": "8.58.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.1.tgz", + "integrity": "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.1", + "@typescript-eslint/types": "^8.58.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.1.tgz", + "integrity": "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.1", + "@typescript-eslint/visitor-keys": "8.58.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.1.tgz", + "integrity": "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.1.tgz", + "integrity": "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.1", + "@typescript-eslint/typescript-estree": "8.58.1", + "@typescript-eslint/utils": "8.58.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz", + "integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.1.tgz", + "integrity": "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.1", + "@typescript-eslint/tsconfig-utils": "8.58.1", + "@typescript-eslint/types": "8.58.1", + "@typescript-eslint/visitor-keys": "8.58.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.1.tgz", + "integrity": "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.1", + "@typescript-eslint/types": "8.58.1", + "@typescript-eslint/typescript-estree": "8.58.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.1.tgz", + "integrity": "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.2.tgz", + "integrity": "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.17", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.17.tgz", + "integrity": "sha512-HdrkN8eVG2CXxeifv/VdJ4A4RSra1DTW8dc/hdxzhGHN8QePs6gKaWM9pHPcpCoxYZJuOZ8drHmbdpLHjCYjLA==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001787", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", + "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.334", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz", + "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", + "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.3.tgz", + "integrity": "sha512-Dnkrylzjof/Az7iNoIQJqD18zTxQZcngir19KJaiRsMnnjpQSVoa6aEg/1Q4hQC+cW90uTlgQYadwL1CYNwFWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "16.2.3", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^7.0.0", + "globals": "16.4.0", + "typescript-eslint": "^8.46.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-next/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ethers": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", + "integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "16.2.3", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.3.tgz", + "integrity": "sha512-9V3zV4oZFza3PVev5/poB9g0dEafVcgNyQ8eTRop8GvxZjV2G15FC5ARuG1eFD42QgeYkzJBJzHghNP8Ad9xtA==", + "license": "MIT", + "dependencies": { + "@next/env": "16.2.3", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.3", + "@next/swc-darwin-x64": "16.2.3", + "@next/swc-linux-arm64-gnu": "16.2.3", + "@next/swc-linux-arm64-musl": "16.2.3", + "@next/swc-linux-x64-gnu": "16.2.3", + "@next/swc-linux-x64-musl": "16.2.3", + "@next/swc-win32-arm64-msvc": "16.2.3", + "@next/swc-win32-x64-msvc": "16.2.3", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz", + "integrity": "sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.1.tgz", + "integrity": "sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.1", + "@typescript-eslint/parser": "8.58.1", + "@typescript-eslint/typescript-estree": "8.58.1", + "@typescript-eslint/utils": "8.58.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/scripts/simplex/tx-frontend/package.json b/scripts/simplex/tx-frontend/package.json new file mode 100644 index 000000000000..dd3ff16d8067 --- /dev/null +++ b/scripts/simplex/tx-frontend/package.json @@ -0,0 +1,27 @@ +{ + "name": "tx-frontend", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "ethers": "^6.16.0", + "next": "16.2.3", + "react": "19.2.4", + "react-dom": "19.2.4" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.3", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/scripts/simplex/tx-frontend/postcss.config.mjs b/scripts/simplex/tx-frontend/postcss.config.mjs new file mode 100644 index 000000000000..61e36849cf7c --- /dev/null +++ b/scripts/simplex/tx-frontend/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/scripts/simplex/tx-frontend/public/chains.json b/scripts/simplex/tx-frontend/public/chains.json new file mode 100644 index 000000000000..991c1ebf7554 --- /dev/null +++ b/scripts/simplex/tx-frontend/public/chains.json @@ -0,0 +1,27 @@ +[ + { + "name": "P-Chain", + "chainId": "P", + "rpcPath": "/ext/bc/P", + "vm": "platformvm", + "consensus": "snowman" + }, + { + "name": "C-Chain", + "chainId": "C", + "rpcPath": "/ext/bc/C/rpc", + "vm": "evm", + "consensus": "snowman" + }, + { + "name": "simplexl1", + "chainId": "Du6NZkrpGtpBxFvqz8rPdie4jhBf63EisQXa1nuiVp6LkX12T", + "rpcPath": "/ext/bc/Du6NZkrpGtpBxFvqz8rPdie4jhBf63EisQXa1nuiVp6LkX12T/rpc", + "subnetId": "NNZsBjfD8VwWoyWuxgr6LdTP2E2xHp9pJeri8icgW7Fq96pfv", + "vm": "subnet-evm", + "consensus": "simplex", + "conversionTx": "HgEKo2P2tCmvH6qvjGG5jKkneZuJazsCAmpWLiTyCD8th1nfE", + "conversionId": "2SuD4tQ39KYazRjTvM2a956ER4GSht9ETsqpYBZR5Ane46AopG", + "validatorCount": 5 + } +] \ No newline at end of file diff --git a/scripts/simplex/tx-frontend/public/file.svg b/scripts/simplex/tx-frontend/public/file.svg new file mode 100644 index 000000000000..004145cddf3f --- /dev/null +++ b/scripts/simplex/tx-frontend/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/simplex/tx-frontend/public/globe.svg b/scripts/simplex/tx-frontend/public/globe.svg new file mode 100644 index 000000000000..567f17b0d7c7 --- /dev/null +++ b/scripts/simplex/tx-frontend/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/simplex/tx-frontend/public/next.svg b/scripts/simplex/tx-frontend/public/next.svg new file mode 100644 index 000000000000..5174b28c565c --- /dev/null +++ b/scripts/simplex/tx-frontend/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/simplex/tx-frontend/public/nodes.json b/scripts/simplex/tx-frontend/public/nodes.json new file mode 100644 index 000000000000..3a1d18721fd7 --- /dev/null +++ b/scripts/simplex/tx-frontend/public/nodes.json @@ -0,0 +1 @@ +["http://127.0.0.1:63082","http://127.0.0.1:63122","http://127.0.0.1:63157","http://127.0.0.1:63192","http://127.0.0.1:63228"] diff --git a/scripts/simplex/tx-frontend/public/vercel.svg b/scripts/simplex/tx-frontend/public/vercel.svg new file mode 100644 index 000000000000..77053960334e --- /dev/null +++ b/scripts/simplex/tx-frontend/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/simplex/tx-frontend/public/window.svg b/scripts/simplex/tx-frontend/public/window.svg new file mode 100644 index 000000000000..b2b2a44f6ebc --- /dev/null +++ b/scripts/simplex/tx-frontend/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/simplex/tx-frontend/tsconfig.json b/scripts/simplex/tx-frontend/tsconfig.json new file mode 100644 index 000000000000..3a13f90a773b --- /dev/null +++ b/scripts/simplex/tx-frontend/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} diff --git a/simplex/BUILD.bazel b/simplex/BUILD.bazel index 9d1585344451..638a5f6a72db 100644 --- a/simplex/BUILD.bazel +++ b/simplex/BUILD.bazel @@ -28,6 +28,7 @@ go_library( "//ids", "//message", "//proto/pb/p2p", + "//snow", "//snow/consensus/simplex", "//snow/consensus/snowman", "//snow/engine/common", diff --git a/simplex/block.go b/simplex/block.go index efaa5e713e5d..531583d40d59 100644 --- a/simplex/block.go +++ b/simplex/block.go @@ -12,10 +12,12 @@ import ( "sync" "github.com/ava-labs/simplex" + "go.uber.org/zap" "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/utils/hashing" + "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/tree" ) @@ -45,12 +47,13 @@ type Block struct { blacklist simplex.Blacklist } -func newBlock(metadata simplex.ProtocolMetadata, blacklist simplex.Blacklist, vmBlock snowman.Block, blockTracker *blockTracker) (*Block, error) { +func newBlock(metadata simplex.ProtocolMetadata, bl simplex.Blacklist, vmBlock snowman.Block, blockTracker *blockTracker) (*Block, error) { + fmt.Println("logger blacklist", bl.String()) block := &Block{ metadata: metadata, vmBlock: vmBlock, blockTracker: blockTracker, - blacklist: blacklist, + blacklist: bl, } bytes, err := block.Bytes() if err != nil { @@ -107,6 +110,11 @@ func (b *Block) Verify(ctx context.Context) (simplex.VerifiedBlock, error) { return nil, fmt.Errorf("failed to verify block: %w", err) } + err := b.blockTracker.vm.SetPreference(ctx, b.vmBlock.ID()) + if err != nil { + return nil, fmt.Errorf("failed to set preference: %w", err) + } + return b, nil } @@ -169,6 +177,10 @@ type blockTracker struct { // handles block acceptance and rejection of inner blocks tree tree.Tree + + logger logging.Logger + + vm block.ChainVM } func newBlockTracker() *blockTracker { @@ -211,6 +223,12 @@ func (bt *blockTracker) verifyAndTrackBlock(ctx context.Context, block *Block) e // track the block bt.simplexDigestsToBlock[block.digest] = block bt.tree.Add(block.vmBlock) + bt.logger.Info("verified and tracked block", + zap.Stringer("blockDigest", block.digest), + zap.Stringer("blockID", block.vmBlock.ID()), + zap.Uint64("round", block.metadata.Round), + zap.Uint64("seq", block.metadata.Seq), + ) return nil } diff --git a/simplex/block_builder.go b/simplex/block_builder.go index cda9c6a4797a..92d0c69730b3 100644 --- a/simplex/block_builder.go +++ b/simplex/block_builder.go @@ -37,11 +37,13 @@ func (b *BlockBuilder) BuildBlock(ctx context.Context, metadata simplex.Protocol return nil, false } + b.log.Debug("Attempting to build block!!!") err := b.waitForPendingBlock(ctx) if err != nil { b.log.Debug("Error waiting for incoming block", zap.Error(err)) continue } + b.log.Debug("Received pending block event, building block!!!") vmBlock, err := b.vm.BuildBlock(ctx) if err != nil { b.log.Info("Error building block", zap.Error(err)) @@ -85,6 +87,8 @@ func (b *BlockBuilder) waitForPendingBlock(ctx context.Context) error { } } +// go run ./scripts/simplex/send_tx/ --nodes=./scripts/simplex/tx-frontend/public/nodes.json --chains=./scripts/simplex/tx-frontend/public/chains.json + // backoff waits for `backoff` duration before returning the next backoff duration. // It doubles the backoff duration each time it is called, up to a maximum of `maxBackoff`. func backoff(ctx context.Context, backoff time.Duration) time.Duration { diff --git a/simplex/block_test.go b/simplex/block_test.go index b0e107105c22..ed6b0d71f9cf 100644 --- a/simplex/block_test.go +++ b/simplex/block_test.go @@ -125,6 +125,11 @@ func TestBlockSerialization(t *testing.T) { } } +func TestPrevBlacklist(t *testing.T) { + b := newTestBlock(t, newBlockConfig{}) + require.True(t, b.blacklist.IsEmpty()) +} + // TestVerifyPrevNotFound attempts to verify a block with a prev digest that is not valid. func TestVerifyPrevNotFound(t *testing.T) { ctx := t.Context() diff --git a/simplex/engine.go b/simplex/engine.go index ace4170cf162..0a2eb951c077 100644 --- a/simplex/engine.go +++ b/simplex/engine.go @@ -15,6 +15,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/proto/pb/p2p" + "github.com/ava-labs/avalanchego/snow" "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/engine/snowman/block" "github.com/ava-labs/avalanchego/snow/validators" @@ -31,6 +32,13 @@ var ( ) type Engine struct { + // nonValidator marks that this node is not a validator + // this is included, but marked as a todo since the e2e tests currently + // try and bootstrap a node that is not a validator(see func `CheckBootstrapIsPossible`). + // The bootstrapped node is a non-validator which is currently not supported. + // TODO: handle non-validators properly + nonValidator bool + // list of NoOpsHandler for messages dropped by engine common.AllGetsServer common.StateSummaryFrontierHandler @@ -55,14 +63,24 @@ type Engine struct { tickInterval time.Duration shutdown chan struct{} shutdownOnce sync.Once + consensusCtx *snow.ConsensusContext } // The VM must be initialized before creating the engine -func NewEngine(ctx context.Context, config *Config) (*Engine, error) { +func NewEngine(ctx context.Context, snowCtx *snow.ConsensusContext, config *Config) (*Engine, error) { if config.Params == nil { return nil, errNilSimplexParameters } + if isNonValidator(config) { + config.Log.Info("Our node is not a validator for the subnet", + zap.Stringer("nodeID", config.Ctx.NodeID), + zap.Stringer("chainID", config.Ctx.ChainID), + zap.Stringer("subnetID", config.Ctx.SubnetID), + ) + return nonValidatingEngine(snowCtx, config) + } + signer, verifier, err := NewBLSAuth(config) if err != nil { return nil, fmt.Errorf("failed to create BLS auth: %w", err) @@ -81,6 +99,8 @@ func NewEngine(ctx context.Context, config *Config) (*Engine, error) { } bt := newBlockTracker() + bt.logger = config.Log + bt.vm = config.VM storage, err := newStorage(ctx, config, qcDeserializer, bt) if err != nil { @@ -139,41 +159,42 @@ func NewEngine(ctx context.Context, config *Config) (*Engine, error) { return nil, err } - return &Engine{ - AllGetsServer: common.NewNoOpAllGetsServer(config.Log), - StateSummaryFrontierHandler: common.NewNoOpStateSummaryFrontierHandler(config.Log), - AcceptedStateSummaryHandler: common.NewNoOpAcceptedStateSummaryHandler(config.Log), - AcceptedFrontierHandler: common.NewNoOpAcceptedFrontierHandler(config.Log), - AcceptedHandler: common.NewNoOpAcceptedHandler(config.Log), - AncestorsHandler: common.NewNoOpAncestorsHandler(config.Log), - PutHandler: common.NewNoOpPutHandler(config.Log), - QueryHandler: common.NewNoOpQueryHandler(config.Log), - ChitsHandler: common.NewNoOpChitsHandler(config.Log), - AppHandler: config.VM, - Connector: config.VM, - vm: config.VM, - - epoch: epoch, - blockDeserializer: blockDeserializer, - quorumDeserializer: qcDeserializer, - logger: config.Log, + engine := newEngine(config, snowCtx) + engine.epoch = epoch + engine.blockDeserializer = blockDeserializer + engine.quorumDeserializer = qcDeserializer - tickInterval: getTickInterval(config.Params), - shutdown: make(chan struct{}, 1), - }, nil + return engine, nil } -func (e *Engine) Start(_ context.Context, _ uint32) error { +func (e *Engine) Start(ctx context.Context, _ uint32) error { + if e.nonValidator { + e.logger.Info("non-validator cannot start simplex engine") + return nil + } + e.logger.Info( "Starting simplex engine", zap.Duration("TickInterval", e.tickInterval), zap.Duration("MaxProposalWait", e.epoch.MaxProposalWait), zap.Duration("MaxRebroadcastWait", e.epoch.MaxRebroadcastWait), ) + + // Set NormalOp BEFORE starting the epoch so that the VM's block builder + // is initialized before the epoch can trigger block building via WaitForEvent. + e.consensusCtx.State.Set(snow.EngineState{ + Type: p2p.EngineType_ENGINE_TYPE_CHAIN, + State: snow.NormalOp, + }) + if err := e.vm.SetState(ctx, snow.NormalOp); err != nil { + return fmt.Errorf("failed to set VM state to NormalOp: %w", err) + } + if err := e.epoch.Start(); err != nil { + e.logger.Error("Failed to start simplex epoch", zap.Error(err)) return fmt.Errorf("failed to start simplex epoch: %w", err) } - + e.logger.Info("Started simplex epoch, starting block builder ticker") go e.tick() return nil } @@ -200,12 +221,21 @@ func (e *Engine) tick() { } func (e *Engine) Simplex(ctx context.Context, nodeID ids.NodeID, msg *p2p.Simplex) error { + if e.nonValidator { + e.logger.Debug("non-validator received simplex message; dropping") + return nil + } + simplexMsg, err := e.p2pToSimplexMessage(ctx, msg) if err != nil { e.logger.Debug("failed to convert p2p message to simplex message", zap.Error(err)) return nil } + e.logger.Debug("received simplex message", + zap.Stringer("from", nodeID), + ) + return e.epoch.HandleMessage(simplexMsg, nodeID[:]) } @@ -269,3 +299,69 @@ func (e *Engine) Shutdown(_ context.Context) error { }) return nil } + +var _ common.BootstrapableEngine = (*NoopBootstrapper)(nil) + +type NoopBootstrapper struct { + *Engine + common.BootstrapTracker +} + +func (t *NoopBootstrapper) Start(ctx context.Context, _ uint32) error { + t.Engine.logger.Info("Starting simplex no-op bootstrapper") + + t.Engine.consensusCtx.State.Set(snow.EngineState{ + Type: p2p.EngineType_ENGINE_TYPE_CHAIN, + State: snow.Bootstrapping, + }) + + if err := t.Engine.vm.SetState(ctx, snow.Bootstrapping); err != nil { + return fmt.Errorf("failed to notify VM the bootstrapper is starting: %w", err) + } + + // We must notify the tracker we have finished bootstrapping + t.BootstrapTracker.Bootstrapped(t.Engine.consensusCtx.ChainID) + + return t.Engine.Start(ctx, 0) +} + +func (*NoopBootstrapper) Clear(_ context.Context) error { + return nil +} + +func newEngine(config *Config, consensusCtx *snow.ConsensusContext) *Engine { + return &Engine{ + AllGetsServer: common.NewNoOpAllGetsServer(config.Log), + StateSummaryFrontierHandler: common.NewNoOpStateSummaryFrontierHandler(config.Log), + AcceptedStateSummaryHandler: common.NewNoOpAcceptedStateSummaryHandler(config.Log), + AcceptedFrontierHandler: common.NewNoOpAcceptedFrontierHandler(config.Log), + AcceptedHandler: common.NewNoOpAcceptedHandler(config.Log), + AncestorsHandler: common.NewNoOpAncestorsHandler(config.Log), + PutHandler: common.NewNoOpPutHandler(config.Log), + QueryHandler: common.NewNoOpQueryHandler(config.Log), + ChitsHandler: common.NewNoOpChitsHandler(config.Log), + AppHandler: config.VM, + Connector: config.VM, + vm: config.VM, + + consensusCtx: consensusCtx, + logger: config.Log, + tickInterval: getTickInterval(config.Params), + shutdown: make(chan struct{}), + } +} + +func nonValidatingEngine(consensusCtx *snow.ConsensusContext, config *Config) (*Engine, error) { + engine := newEngine(config, consensusCtx) + engine.nonValidator = true + return engine, nil +} + +func isNonValidator(config *Config) bool { + for _, node := range config.Params.InitialValidators { + if node.NodeID == config.Ctx.NodeID { + return false + } + } + return true +} diff --git a/simplex/engine_test.go b/simplex/engine_test.go index 9939975ac999..eac6c763fc83 100644 --- a/simplex/engine_test.go +++ b/simplex/engine_test.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/avalanchego/snow/consensus/snowman" "github.com/ava-labs/avalanchego/snow/engine/common" "github.com/ava-labs/avalanchego/snow/networking/sender/sendermock" + "github.com/ava-labs/avalanchego/snow/snowtest" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/version" @@ -99,7 +100,9 @@ func TestSimplexEngineNilParameters(t *testing.T) { config := configs[0] config.Params = nil - _, err := NewEngine(ctx, config) + snowCtx := snowtest.Context(t, ids.GenerateTestID()) + consensusCtx := snowtest.ConsensusContext(snowCtx) + _, err := NewEngine(ctx, consensusCtx, config) require.ErrorIs(t, err, errNilSimplexParameters) } @@ -192,7 +195,10 @@ func TestSimplexEngineRejectsMalformedSimplexMessages(t *testing.T) { Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). AnyTimes() - engine, err := NewEngine(ctx, config) + snowCtx := snowtest.Context(t, ids.GenerateTestID()) + consensusCtx := snowtest.ConsensusContext(snowCtx) + + engine, err := NewEngine(ctx, consensusCtx, config) require.NoError(t, err) config.VM.(*wrappedVM).ParseBlockF = func(_ context.Context, _ []byte) (snowman.Block, error) { @@ -858,7 +864,9 @@ func setupEngine(t *testing.T) (*Engine, []*Config) { config := configs[0] config.Sender.(*sendermock.ExternalSender).EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - engine, err := NewEngine(ctx, config) + snowCtx := snowtest.Context(t, ids.GenerateTestID()) + consensusCtx := snowtest.ConsensusContext(snowCtx) + engine, err := NewEngine(ctx, consensusCtx, config) require.NoError(t, err) // ensure any go-routines started by the engine are cleaned up after the test finishes diff --git a/simplex/storage.go b/simplex/storage.go index b7a740d2c03d..0482775daec9 100644 --- a/simplex/storage.go +++ b/simplex/storage.go @@ -217,6 +217,7 @@ func getGenesisBlock(ctx context.Context, config *Config, blockTracker *blockTra metadata: genesisMetadata, blockTracker: blockTracker, vmBlock: snowmanGenesis, + // blacklist: simplex.NewBlacklist(uint16(len(config.Params.InitialValidators))), } // set the digest diff --git a/snow/networking/handler/handler.go b/snow/networking/handler/handler.go index bd3b5667717d..d0ee648c8108 100644 --- a/snow/networking/handler/handler.go +++ b/snow/networking/handler/handler.go @@ -249,8 +249,10 @@ func (h *handler) Start(ctx context.Context, recoverPanic bool) { return } - h.nf = common.NewNotificationForwarder(h, h.subscription, h.ctx.Log) - h.cn.OnChange = h.nf.CheckForEvent + if h.cn != nil { + h.nf = common.NewNotificationForwarder(h, h.subscription, h.ctx.Log) + h.cn.OnChange = h.nf.CheckForEvent + } h.ctx.Lock.Lock() err = gear.Start(ctx, 0) @@ -876,16 +878,16 @@ func (h *handler) handleChanMsg(msg *message.InboundMessage) error { // execution (may change during execution) isNormalOp = h.ctx.State.Get().State == snow.NormalOp ) - if h.ctx.Log.Enabled(logging.Verbo) { - h.ctx.Log.Verbo("forwarding chan message to consensus", - zap.String("messageOp", op), - zap.Stringer("message", body), - ) - } else { - h.ctx.Log.Debug("forwarding chan message to consensus", - zap.String("messageOp", op), - ) - } + // if h.ctx.Log.Enabled(logging.Verbo) { + // h.ctx.Log.Verbo("forwarding chan message to consensus", + // zap.String("messageOp", op), + // zap.Stringer("message", body), + // ) + // } else { + // h.ctx.Log.Debug("forwarding chan message to consensus", + // zap.String("messageOp", op), + // ) + // } h.ctx.Lock.Lock() lockAcquiredTime := h.clock.Time() defer func() { @@ -904,9 +906,10 @@ func (h *handler) handleChanMsg(msg *message.InboundMessage) error { h.metrics.messageHandlingTime.With(labels).Add(float64(handlingTime)) msg.OnFinishedHandling() - h.ctx.Log.Debug("finished handling chan message", - zap.String("messageOp", op), - ) + // Simplex doesn't need to handle gossip messages. + // h.ctx.Log.Debug("finished handling chan message", + // zap.String("messageOp", op), + // ) if lockingTime+handlingTime > syncProcessingTimeWarnLimit && isNormalOp { h.ctx.Log.Warn("handling chan message took longer than expected", zap.Duration("lockingTime", lockingTime), diff --git a/snow/networking/router/routertest/BUILD.bazel b/snow/networking/router/routertest/BUILD.bazel new file mode 100644 index 000000000000..1a2b34b49b42 --- /dev/null +++ b/snow/networking/router/routertest/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "routertest", + srcs = ["router.go"], + importpath = "github.com/ava-labs/avalanchego/snow/networking/router/routertest", + visibility = ["//visibility:public"], + deps = [ + "//ids", + "//snow/networking/benchlist", + "//snow/networking/router", + "//snow/networking/timeout", + "//utils/logging", + "//utils/set", + "//utils/timer", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_stretchr_testify//require", + ], +) diff --git a/snow/networking/router/routertest/router.go b/snow/networking/router/routertest/router.go new file mode 100644 index 000000000000..b116ec471a15 --- /dev/null +++ b/snow/networking/router/routertest/router.go @@ -0,0 +1,56 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package routertest + +import ( + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/networking/benchlist" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/snow/networking/timeout" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/utils/timer" +) + +// New creates a new initialized Router for testing with its own timeout manager. +// The timeout manager is automatically cleaned up when the test finishes. +func New(t testing.TB) *router.ChainRouter { + tm, err := timeout.NewManager( + &timer.AdaptiveTimeoutConfig{ + InitialTimeout: 500 * time.Millisecond, + MinimumTimeout: 500 * time.Millisecond, + MaximumTimeout: 500 * time.Millisecond, + TimeoutCoefficient: 1, + TimeoutHalflife: 5 * time.Minute, + }, + benchlist.NewNoBenchlist(), + prometheus.NewRegistry(), + prometheus.NewRegistry(), + ) + require.NoError(t, err) + + go tm.Dispatch() + t.Cleanup(tm.Stop) + + chainRouter := &router.ChainRouter{} + require.NoError(t, chainRouter.Initialize( + ids.EmptyNodeID, + logging.NoLog{}, + tm, + time.Second, + set.Set[ids.ID]{}, + true, + set.Set[ids.ID]{}, + nil, + router.HealthConfig{}, + prometheus.NewRegistry(), + )) + return chainRouter +} diff --git a/tests/e2e/BUILD.bazel b/tests/e2e/BUILD.bazel index e6ce5b8d8435..ba57b8619b86 100644 --- a/tests/e2e/BUILD.bazel +++ b/tests/e2e/BUILD.bazel @@ -19,6 +19,7 @@ go_test( "//tests/e2e/c", "//tests/e2e/faultinjection", "//tests/e2e/p", + "//tests/e2e/s", "//tests/e2e/vms", "//tests/e2e/x", "//tests/e2e/x/transfer", diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index e945ac40f156..16892f87b25e 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -23,6 +23,7 @@ import ( "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/graft/coreth/plugin/evm" + "github.com/ava-labs/avalanchego/tests/e2e/s" "github.com/ava-labs/avalanchego/tests/e2e/vms" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" @@ -48,7 +49,9 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { nodeCount, err := flagVars.NodeCount() require.NoError(tc, err) nodes := tmpnet.NewNodesOrPanic(nodeCount) - subnets := vms.XSVMSubnetsOrPanic(nodes...) + xsvmSubnets := vms.XSVMSubnetsOrPanic(nodes...) + simplexSubnet := s.NewSimplexSubnetOrPanic(nodes...) + subnets := append(xsvmSubnets, simplexSubnet) upgradeToActivate := upgradetest.Latest if !flagVars.ActivateLatest() { diff --git a/tests/e2e/s/BUILD.bazel b/tests/e2e/s/BUILD.bazel new file mode 100644 index 000000000000..49eb8cdf7f44 --- /dev/null +++ b/tests/e2e/s/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "s", + srcs = [ + "simplex.go", + "utils.go", + ], + importpath = "github.com/ava-labs/avalanchego/tests/e2e/s", + visibility = ["//visibility:public"], + deps = [ + "//ids", + "//snow/consensus/simplex", + "//tests/fixture/e2e", + "//tests/fixture/tmpnet", + "//utils/constants", + "//utils/crypto/secp256k1", + "//utils/set", + "//utils/units", + "//vms/example/xsvm/api", + "//vms/example/xsvm/genesis", + "//vms/example/xsvm/tx", + "@com_github_onsi_ginkgo_v2//:ginkgo", + "@com_github_stretchr_testify//require", + "@org_uber_go_zap//:zap", + ], +) diff --git a/tests/e2e/s/simplex.go b/tests/e2e/s/simplex.go new file mode 100644 index 000000000000..60857b667a08 --- /dev/null +++ b/tests/e2e/s/simplex.go @@ -0,0 +1,205 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package s + +import ( + "math" + "time" + + "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/snow/consensus/simplex" + "github.com/ava-labs/avalanchego/tests/fixture/e2e" + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/avalanchego/vms/example/xsvm/api" + "github.com/ava-labs/avalanchego/vms/example/xsvm/genesis" + "github.com/ava-labs/avalanchego/vms/example/xsvm/tx" +) + +var simplexSubnetName = "simplex" + +func NewSimplexSubnetOrPanic(nodes ...*tmpnet.Node) *tmpnet.Subnet { + key, err := secp256k1.NewPrivateKey() + if err != nil { + panic(err) + } + + if len(nodes) == 0 { + panic("a subnet must be validated by at least one node") + } + + genesisBytes, err := genesis.Codec.Marshal(genesis.CodecVersion, &genesis.Genesis{ + Timestamp: time.Now().Unix(), + Allocations: []genesis.Allocation{ + { + Address: key.Address(), + Balance: math.MaxUint64, + }, + }, + }) + if err != nil { + panic(err) + } + + // declare the initial validator set for the simplex subnet + initialValidators := make([]simplex.ValidatorInfo, 0, len(nodes)) + for _, node := range nodes { + key, err := node.GetBLSSigningKey() + if err != nil { + panic(err) + } + + initialValidators = append(initialValidators, simplex.ValidatorInfo{ + NodeID: node.NodeID, + PublicKey: key.PublicKey().Compress(), + }) + } + + return &tmpnet.Subnet{ + Name: simplexSubnetName, + Config: tmpnet.ConfigMap{ + "simplexParameters": map[string]interface{}{ + "initialValidators": initialValidators, + }, + }, + Chains: []*tmpnet.Chain{ + { + VMID: constants.XSVMID, + Genesis: genesisBytes, + PreFundedKey: key, + VersionArgs: []string{"version-json"}, + }, + }, + ValidatorIDs: tmpnet.NodesToIDs(nodes...), + } +} + +var _ = e2e.DescribeSimplex("Create a Simplex [L1]", func() { + tc := e2e.NewTestContext() + require := require.New(tc) + + ginkgo.It("Issue and Finalize a Transaction", func() { + network := e2e.GetEnv(tc).GetNetwork() + + simplexSubnet := network.GetSubnet(simplexSubnetName) + require.NotNil(simplexSubnet) + + simplexChain := simplexSubnet.Chains[0] + simplexValidators := getNodesWithIDs(network.Nodes, set.Of(simplexSubnet.ValidatorIDs...)) + require.NotEmpty(simplexValidators) + + sortNodes(simplexValidators) + + // advance two rounds + issueAndConfirmTx(tc, simplexChain, simplexValidators) + issueAndConfirmTx(tc, simplexChain, simplexValidators) + + _ = e2e.CheckBootstrapIsPossible(tc, network) + }) +}) + +// Builds and proposes a new block by issuing a transfer transaction from the leader of the next round. +// Verifies that all nodes have accepted the block and advanced to the next round. +func issueAndConfirmTx(tc *e2e.GinkgoTestContext, chain *tmpnet.Chain, nodes []*tmpnet.Node) { + require := require.New(tc) + + // grab the current round + client := api.NewClient(nodes[0].GetAccessibleURI(), chain.ChainID.String()) + _, latestBlock, err := client.LastAccepted(tc.DefaultContext()) + require.NoError(err) + + round := latestBlock.Height + 1 + leader := getLeaderForRound(nodes, round) + tc.Log().Info("current height and round", + zap.Uint64("height", latestBlock.Height), + zap.Uint64("round", round), + zap.Stringer("leader", leader.NodeID), + ) + + // record the nonces of all nodes before issuing the tx + nonces := make(map[ids.NodeID]uint64) + for _, node := range nodes { + client = api.NewClient(node.GetAccessibleURI(), chain.ChainID.String()) + nonce, err := client.Nonce(tc.DefaultContext(), chain.PreFundedKey.Address()) + require.NoError(err) + nonces[node.NodeID] = nonce + } + + // issue a transfer transaction to the leader + leaderURI := leader.GetAccessibleURI() + tc.Log().Info("issuing XSVM transfer transaction for simplex subnet", + zap.Stringer("leader", leader.NodeID), + zap.String("leaderURI", leaderURI), + ) + recipientKey := e2e.NewPrivateKey(tc) + + // create and sign the transaction + utx := &tx.Transfer{ + ChainID: chain.ChainID, + Nonce: nonces[leader.NodeID], + MaxFee: 0, + AssetID: chain.ChainID, + Amount: units.Schmeckle, + To: recipientKey.Address(), + } + stx, err := tx.Sign(utx, chain.PreFundedKey) + require.NoError(err) + txID, err := stx.ID() + require.NoError(err) + + // issue txs to all nodes, because XSVM does not have mempool gossiping + for i, node := range nodes { + client = api.NewClient(node.GetAccessibleURI(), chain.ChainID.String()) + _, err := client.IssueTx(tc.DefaultContext(), stx) + require.NoError(err, "node %d failed to issue tx", i) + } + + tc.Log().Info("successfully issued XSVM transfer transactions", + zap.Stringer("txID", txID), + zap.Uint64("round", round), + ) + + tc.By("checking all nodes have accepted the tx") + address := chain.PreFundedKey.Address() + for _, node := range nodes { + client = api.NewClient(node.GetAccessibleURI(), chain.ChainID.String()) + err = api.AwaitTxAccepted(tc.DefaultContext(), client, address, nonces[node.NodeID], api.DefaultPollingInterval) + require.NoError(err, "node %s failed to accept tx in round %d", node.NodeID, round) + + balance, err := client.Balance(tc.DefaultContext(), recipientKey.Address(), chain.ChainID) + require.NoError(err) + require.Equal(units.Schmeckle, balance) + + _, statelessBlock, err := client.LastAccepted(tc.DefaultContext()) + require.NoError(err) + + require.Len(statelessBlock.Txs, 1) + require.Equal(statelessBlock.Txs[0], stx) + + tc.Log().Info("node has accepted the tx", + zap.Stringer("node", node.NodeID), + zap.Stringer("txID", txID), + zap.Uint64("height", statelessBlock.Height), + zap.Uint64("round", round), + ) + } +} + +// Retrieve the nodes corresponding to the provided IDs +func getNodesWithIDs(nodes []*tmpnet.Node, nodeIDs set.Set[ids.NodeID]) []*tmpnet.Node { + desiredNodes := make([]*tmpnet.Node, 0, len(nodeIDs)) + for _, node := range nodes { + if nodeIDs.Contains(node.NodeID) { + desiredNodes = append(desiredNodes, node) + } + } + return desiredNodes +} diff --git a/tests/e2e/s/utils.go b/tests/e2e/s/utils.go new file mode 100644 index 000000000000..e0b80ef41cd7 --- /dev/null +++ b/tests/e2e/s/utils.go @@ -0,0 +1,21 @@ +// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package s + +import ( + "bytes" + "slices" + + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" +) + +func getLeaderForRound(nodes []*tmpnet.Node, round uint64) *tmpnet.Node { + return nodes[round%uint64(len(nodes))] +} + +func sortNodes(nodes []*tmpnet.Node) { + slices.SortFunc(nodes, func(a, b *tmpnet.Node) int { + return bytes.Compare(a.NodeID[:], b.NodeID[:]) + }) +} diff --git a/tests/fixture/e2e/describe.go b/tests/fixture/e2e/describe.go index 47ea473e11aa..e0dbf5435267 100644 --- a/tests/fixture/e2e/describe.go +++ b/tests/fixture/e2e/describe.go @@ -37,3 +37,9 @@ func DescribeCChain(text string, args ...interface{}) bool { args = append(args, ginkgo.Label("c")) return ginkgo.Describe("[C-Chain] "+text, args...) } + +// DescribeSimplex annotates the tests for Simplex. +func DescribeSimplex(text string, args ...interface{}) bool { + args = append(args, ginkgo.Label("s")) + return ginkgo.Describe("[Simplex] "+text, args...) +} diff --git a/tests/fixture/tmpnet/network.go b/tests/fixture/tmpnet/network.go index 0aafdd28403c..7581014ec6ca 100644 --- a/tests/fixture/tmpnet/network.go +++ b/tests/fixture/tmpnet/network.go @@ -615,7 +615,6 @@ func (n *Network) GetSubnet(name string) *Subnet { // subnet creation so that they can be restarted if already running. func (n *Network) CreateSubnets(ctx context.Context, log logging.Logger, apiNode *Node) (set.Set[ids.NodeID], error) { createdSubnets := make([]*Subnet, 0, len(n.Subnets)) - apiURI := apiNode.GetAccessibleURI() for _, subnet := range n.Subnets { if len(subnet.ValidatorIDs) == 0 { return nil, stacktrace.Errorf("subnet %s needs at least one validator", subnet.SubnetID) @@ -640,7 +639,7 @@ func (n *Network) CreateSubnets(ctx context.Context, log logging.Logger, apiNode } // Create the subnet on the network - if err := subnet.Create(ctx, apiURI); err != nil { + if err := subnet.Create(ctx, apiNode.GetAccessibleURI()); err != nil { return nil, stacktrace.Wrap(err) } @@ -701,20 +700,20 @@ func (n *Network) CreateSubnets(ctx context.Context, log logging.Logger, apiNode validatorNodes = append(validatorNodes, node) } - if err := subnet.AddValidators(ctx, log, apiURI, validatorNodes...); err != nil { + if err := subnet.AddValidators(ctx, log, apiNode.URI, validatorNodes...); err != nil { return nil, stacktrace.Wrap(err) } } // Wait for nodes to become subnet validators - pChainClient := platformvm.NewClient(apiURI) + pChainClient := platformvm.NewClient(apiNode.URI) for _, subnet := range createdSubnets { if err := WaitForActiveValidators(ctx, log, pChainClient, subnet); err != nil { return nil, stacktrace.Wrap(err) } // It should now be safe to create chains for the subnet - if err := subnet.CreateChains(ctx, log, apiURI); err != nil { + if err := subnet.CreateChains(ctx, log, apiNode.URI); err != nil { return nil, stacktrace.Wrap(err) } diff --git a/tests/fixture/tmpnet/node.go b/tests/fixture/tmpnet/node.go index 5e4c637b1ef9..9e9e9580dd49 100644 --- a/tests/fixture/tmpnet/node.go +++ b/tests/fixture/tmpnet/node.go @@ -273,6 +273,19 @@ func (n *Node) EnsureBLSSigningKey() error { return nil } +func (n *Node) GetBLSSigningKey() (*localsigner.LocalSigner, error) { + signingKey := n.Flags[config.StakingSignerKeyContentKey] + signingKeyBytes, err := base64.StdEncoding.DecodeString(signingKey) + if err != nil { + return nil, stacktrace.Wrap(err) + } + signer, err := localsigner.FromBytes(signingKeyBytes) + if err != nil { + return nil, stacktrace.Wrap(err) + } + return signer, nil +} + // Ensures a staking keypair is generated if not already present. func (n *Node) EnsureStakingKeypair() error { keyKey := config.StakingTLSKeyContentKey diff --git a/utils/crypto/bls/threshold/BUILD.bazel b/utils/crypto/bls/threshold/BUILD.bazel new file mode 100644 index 000000000000..f6c0bac9371a --- /dev/null +++ b/utils/crypto/bls/threshold/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("//.bazel:defs.bzl", "go_test") + +go_library( + name = "threshold", + srcs = [ + "keygen.go", + "lagrange.go", + "scheme.go", + ], + importpath = "github.com/ava-labs/avalanchego/utils/crypto/bls/threshold", + visibility = ["//visibility:public"], + deps = [ + "//utils/crypto/bls", + "//utils/crypto/bls/signer/localsigner", + "@com_github_supranational_blst//bindings/go", + ], +) + +go_test( + name = "threshold_test", + srcs = [ + "lagrange_test.go", + "scheme_test.go", + "threshold_test.go", + ], + embed = [":threshold"], + deps = [ + "//utils/crypto/bls", + "@com_github_stretchr_testify//require", + ], +) diff --git a/utils/crypto/bls/threshold/keygen.go b/utils/crypto/bls/threshold/keygen.go new file mode 100644 index 000000000000..634812c5caa4 --- /dev/null +++ b/utils/crypto/bls/threshold/keygen.go @@ -0,0 +1,92 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package threshold + +import ( + "fmt" + "math/big" + + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/crypto/bls/signer/localsigner" + + blst "github.com/supranational/blst/bindings/go" +) + +// GenerateKeys creates a threshold key set from a deterministic seed. +// +// Returns: +// - scheme: Scheme with group PK, threshold, N (safe to distribute to all nodes) +// - signers: n LocalSigners whose secret keys are polynomial evaluations p(i+1) +// - masterSK: the master secret key a_0 = p(0) (for test assertions only) +// +// The i-th signer (0-indexed) holds the key for Shamir index i+1 (1-based). +// +// POC only: all nodes sharing the seed can derive all keys. +// Production: replace with DKG (masterSK is never known to anyone). +func GenerateKeys(n, t int, seed [32]byte) (*Scheme, []*localsigner.LocalSigner, *blst.SecretKey, error) { + if t < 1 { + return nil, nil, nil, fmt.Errorf("threshold must be >= 1, got %d", t) + } + if n < t { + return nil, nil, nil, fmt.Errorf("n (%d) must be >= threshold (%d)", n, t) + } + + // Generate t polynomial coefficients deterministically from seed. + // Each coefficient a_k is a secret key derived from KeyGen(seed, info_k). + // The coefficients do not define the secret shares directly, they define the polynomial.. + coefficients := make([]*blst.SecretKey, t) + for k := 0; k < t; k++ { + info := []byte(fmt.Sprintf("threshold-coeff-%d", k)) + sk := blst.KeyGen(seed[:], info) + if sk == nil { + return nil, nil, nil, fmt.Errorf("failed to generate coefficient %d", k) + } + coefficients[k] = sk + } + + // Convert coefficients to big.Int for polynomial evaluation. + coeffBig := make([]*big.Int, t) + for k, sk := range coefficients { + coeffBig[k] = new(big.Int).SetBytes(sk.Serialize()) + } + + // Evaluate p(i) for i = 1..n to produce each node's secret key share. + signers := make([]*localsigner.LocalSigner, n) + for i := 0; i < n; i++ { + x := int64(i + 1) // 1-based evaluation point + shareScalar := evaluatePolynomial(coeffBig, x) + + // Convert big.Int to 32-byte big-endian for localsigner.FromBytes. + shareBytes := make([]byte, 32) + b := shareScalar.Bytes() + copy(shareBytes[32-len(b):], b) + + signer, err := localsigner.FromBytes(shareBytes) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to create signer for index %d: %w", i+1, err) + } + signers[i] = signer + } + + // Group public key = g1^{a_0}. + groupPK := new(bls.PublicKey).From(coefficients[0]) + + scheme := NewScheme(t, n, groupPK) + return scheme, signers, coefficients[0], nil +} + +// evaluatePolynomial evaluates p(x) = a_0 + a_1*x + a_2*x^2 + ... + a_{t-1}*x^{t-1} mod r. +// Uses Horner's method for efficiency. +func evaluatePolynomial(coefficients []*big.Int, x int64) *big.Int { + xBig := big.NewInt(x) + result := new(big.Int).Set(coefficients[len(coefficients)-1]) + + for k := len(coefficients) - 2; k >= 0; k-- { + result.Mul(result, xBig) + result.Add(result, coefficients[k]) + result.Mod(result, groupOrder) + } + + return result +} diff --git a/utils/crypto/bls/threshold/lagrange.go b/utils/crypto/bls/threshold/lagrange.go new file mode 100644 index 000000000000..74b81f44100a --- /dev/null +++ b/utils/crypto/bls/threshold/lagrange.go @@ -0,0 +1,103 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package threshold + +import ( + "errors" + "fmt" + "math/big" + + blst "github.com/supranational/blst/bindings/go" +) + +// BLS12-381 scalar field order r. +var groupOrder = new(big.Int).SetBytes([]byte{ + 0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, + 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05, + 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, +}) + +var ( + errDuplicateIndex = errors.New("duplicate signer index") + errZeroIndex = errors.New("signer index must be >= 1") +) + +// lagrangeCoefficients computes the Lagrange basis polynomial values at x=0 +// for the given set of 1-based evaluation indices. +// +// For each index x_i in the set, the coefficient is: +// +// L_i(0) = prod_{j != i}( x_j / (x_j - x_i) ) mod r +// +// All indices must be >= 1 and distinct. +// Returns one blst.Scalar per index, in the same order as the input. +func lagrangeCoefficients(indices []int) ([]blst.Scalar, error) { + n := len(indices) + + // Validate indices. + seen := make(map[int]struct{}, n) + for _, idx := range indices { + if idx < 1 { + return nil, fmt.Errorf("%w: got %d", errZeroIndex, idx) + } + if _, exists := seen[idx]; exists { + return nil, fmt.Errorf("%w: index %d", errDuplicateIndex, idx) + } + seen[idx] = struct{}{} + } + + coeffs := make([]blst.Scalar, n) + for i := 0; i < n; i++ { + num := big.NewInt(1) + den := big.NewInt(1) + + xi := big.NewInt(int64(indices[i])) + for j := 0; j < n; j++ { + if i == j { + continue + } + xj := big.NewInt(int64(indices[j])) + + // num *= x_j + num.Mul(num, xj) + num.Mod(num, groupOrder) + + // den *= (x_j - x_i) + diff := new(big.Int).Sub(xj, xi) + diff.Mod(diff, groupOrder) + den.Mul(den, diff) + den.Mod(den, groupOrder) + } + + // coeff = num * den^{-1} mod r + denInv := new(big.Int).ModInverse(den, groupOrder) + if denInv == nil { + return nil, fmt.Errorf("modular inverse failed for index %d (degenerate indices)", indices[i]) + } + coeff := new(big.Int).Mul(num, denInv) + coeff.Mod(coeff, groupOrder) + + scalar := bigIntToScalar(coeff) + if scalar == nil { + return nil, fmt.Errorf("failed to convert Lagrange coefficient to scalar for index %d", indices[i]) + } + coeffs[i] = *scalar + } + + return coeffs, nil +} + +// bigIntToScalar converts a non-negative big.Int (assumed mod r) to a blst.Scalar. +// The big.Int is serialized as a 32-byte big-endian value. +func bigIntToScalar(n *big.Int) *blst.Scalar { + b := n.Bytes() + + // Zero-pad to 32 bytes on the left. + padded := make([]byte, 32) + copy(padded[32-len(b):], b) + + var s blst.Scalar + return s.FromBEndian(padded) +} diff --git a/utils/crypto/bls/threshold/lagrange_test.go b/utils/crypto/bls/threshold/lagrange_test.go new file mode 100644 index 000000000000..e0c20f6caf90 --- /dev/null +++ b/utils/crypto/bls/threshold/lagrange_test.go @@ -0,0 +1,159 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package threshold + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/require" +) + +// TestLagrangeCoefficients verifies Lagrange coefficient computation with a known case. +// For indices {1, 2, 3} with threshold 3, we can verify the coefficients manually: +// +// L_1(0) = (2*3) / ((2-1)*(3-1)) = 6/2 = 3 +// L_2(0) = (1*3) / ((1-2)*(3-2)) = 3/(-1) = -3 mod r +// L_3(0) = (1*2) / ((1-3)*(2-3)) = 2/((-2)*(-1)) = 2/2 = 1 +func TestLagrangeCoefficients(t *testing.T) { + coeffs, err := lagrangeCoefficients([]int{1, 2, 3}) + require.NoError(t, err) + require.Len(t, coeffs, 3) + + // L_1(0) = 3 + c1Bytes := coeffs[0].Serialize() + require.Equal(t, byte(3), c1Bytes[31]) + for i := 0; i < 31; i++ { + require.Equal(t, byte(0), c1Bytes[i]) + } + + // L_3(0) = 1 + c3Bytes := coeffs[2].Serialize() + require.Equal(t, byte(1), c3Bytes[31]) + for i := 0; i < 31; i++ { + require.Equal(t, byte(0), c3Bytes[i]) + } + + // L_2(0) = -3 mod r = r - 3 + // Verify by checking that the sum L_1 + L_2 + L_3 = 1 (property of Lagrange at x=0 with p(0)=1 for p(x)=1). + // Actually the sum of Lagrange coefficients at x=0 always equals 1 for any set of indices. + // So L_2 = 1 - 3 - 1 = -3 mod r +} + +// TestLagrangeZeroIndex verifies that index 0 is rejected. +func TestLagrangeZeroIndex(t *testing.T) { + _, err := lagrangeCoefficients([]int{0, 1, 2}) + require.ErrorIs(t, err, errZeroIndex) +} + +// TestLagrangeDuplicateIndex verifies that duplicate indices are rejected. +func TestLagrangeDuplicateIndex(t *testing.T) { + _, err := lagrangeCoefficients([]int{1, 1, 2}) + require.ErrorIs(t, err, errDuplicateIndex) +} + +// TestLagrangeNegativeIndex verifies that negative indices are rejected. +func TestLagrangeNegativeIndex(t *testing.T) { + _, err := lagrangeCoefficients([]int{-1, 1, 2}) + require.ErrorIs(t, err, errZeroIndex) +} + +// TestLagrangeSingleIndex verifies coefficients for a single index. +// With only one index, L_1(0) = 1 (the product is empty, so it's 1). +func TestLagrangeSingleIndex(t *testing.T) { + coeffs, err := lagrangeCoefficients([]int{5}) + require.NoError(t, err) + require.Len(t, coeffs, 1) + + // L_5(0) with no other indices = 1 + cBytes := coeffs[0].Serialize() + require.Equal(t, byte(1), cBytes[31]) + for i := 0; i < 31; i++ { + require.Equal(t, byte(0), cBytes[i]) + } +} + +// TestLagrangeCoefficientsSumToOne verifies that Lagrange coefficients always +// sum to 1 mod r. This is a fundamental property: for any polynomial p(x) = 1 +// (constant), interpolation at x=0 must return 1, so the weights must sum to 1. +func TestLagrangeCoefficientsSumToOne(t *testing.T) { + testCases := [][]int{ + {1, 2, 3}, + {1, 3, 5}, + {2, 4, 6, 8}, + {1, 2, 3, 4, 5}, + {10, 20, 30}, + } + + one := big.NewInt(1) + + for _, indices := range testCases { + coeffs, err := lagrangeCoefficients(indices) + require.NoError(t, err) + + sum := new(big.Int) + for _, c := range coeffs { + b := c.Serialize() + val := new(big.Int).SetBytes(b) + sum.Add(sum, val) + sum.Mod(sum, groupOrder) + } + + require.Equal(t, one, sum, "coefficients for indices %v do not sum to 1", indices) + } +} + +// TestLagrangeNonContiguousIndices verifies that non-contiguous indices work correctly. +// The indices don't need to be 1..n — any distinct positive integers work. +func TestLagrangeNonContiguousIndices(t *testing.T) { + coeffs, err := lagrangeCoefficients([]int{3, 7, 11}) + require.NoError(t, err) + require.Len(t, coeffs, 3) + + // Verify sum = 1. + sum := new(big.Int) + for _, c := range coeffs { + b := c.Serialize() + val := new(big.Int).SetBytes(b) + sum.Add(sum, val) + sum.Mod(sum, groupOrder) + } + require.Equal(t, big.NewInt(1), sum) +} + +// TestLagrangeOrderIndependence verifies that the coefficients correspond to +// the correct index regardless of input ordering. +func TestLagrangeOrderIndependence(t *testing.T) { + coeffs1, err := lagrangeCoefficients([]int{1, 2, 3}) + require.NoError(t, err) + + coeffs2, err := lagrangeCoefficients([]int{3, 1, 2}) + require.NoError(t, err) + + // coeffs1[0] is for index 1, coeffs2[1] is for index 1 — should match. + require.Equal(t, coeffs1[0].Serialize(), coeffs2[1].Serialize()) + // coeffs1[1] is for index 2, coeffs2[2] is for index 2. + require.Equal(t, coeffs1[1].Serialize(), coeffs2[2].Serialize()) + // coeffs1[2] is for index 3, coeffs2[0] is for index 3. + require.Equal(t, coeffs1[2].Serialize(), coeffs2[0].Serialize()) +} + +// TestBigIntToScalar verifies the conversion helper. +func TestBigIntToScalar(t *testing.T) { + // Small value. + s := bigIntToScalar(big.NewInt(42)) + require.NotNil(t, s) + b := s.Serialize() + require.Equal(t, byte(42), b[31]) + for i := 0; i < 31; i++ { + require.Equal(t, byte(0), b[i]) + } + + // Larger value. + s = bigIntToScalar(big.NewInt(256)) + require.NotNil(t, s) + b = s.Serialize() + require.Equal(t, byte(1), b[30]) + require.Equal(t, byte(0), b[31]) +} diff --git a/utils/crypto/bls/threshold/scheme.go b/utils/crypto/bls/threshold/scheme.go new file mode 100644 index 000000000000..122daf0e077b --- /dev/null +++ b/utils/crypto/bls/threshold/scheme.go @@ -0,0 +1,119 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package threshold + +import ( + "errors" + "fmt" + + "github.com/ava-labs/avalanchego/utils/crypto/bls" + + blst "github.com/supranational/blst/bindings/go" +) + +var ( + errInsufficientPartials = errors.New("not enough partial signatures to reconstruct") + errInvalidPartialSig = errors.New("invalid partial signature") +) + +// PartialSig is a threshold partial signature from one node. +type PartialSig struct { + // Index is the 1-based Shamir evaluation point of the signer. + // Derived from the signer's position in the canonical validator list + 1. + Index int + // Sig is the compressed BLS signature in G2 (96 bytes). + Sig []byte +} + +// Scheme holds the threshold parameters and group public key. +// It provides reconstruction and verification of threshold signatures. +type Scheme struct { + Threshold int + N int + GroupPK *bls.PublicKey +} + +// NewScheme creates a Scheme from config parameters. +// Used at runtime when keys already exist and you just need reconstruction/verification. +func NewScheme(threshold, n int, groupPK *bls.PublicKey) *Scheme { + return &Scheme{ + Threshold: threshold, + N: n, + GroupPK: groupPK, + } +} + +// HasEnough returns true if count >= threshold. +func (s *Scheme) HasEnough(count int) bool { + return count >= s.Threshold +} + +// Remaining returns the number of additional partial sigs needed. +// Returns 0 if count already meets or exceeds the threshold. +func (s *Scheme) Remaining(count int) int { + if count >= s.Threshold { + return 0 + } + return s.Threshold - count +} + +// Reconstruct combines t partial signatures via Lagrange interpolation in G2. +// Returns the reconstructed threshold signature as a compressed G2 point (96 bytes). +func (s *Scheme) Reconstruct(partials []PartialSig) ([]byte, error) { + if len(partials) < s.Threshold { + return nil, fmt.Errorf( + "%w: have %d, need %d", + errInsufficientPartials, len(partials), s.Threshold, + ) + } + + // Extract indices for Lagrange computation. + indices := make([]int, len(partials)) + for i, p := range partials { + indices[i] = p.Index + } + + // Compute Lagrange coefficients. + coeffs, err := lagrangeCoefficients(indices) + if err != nil { + return nil, fmt.Errorf("failed to compute Lagrange coefficients: %w", err) + } + + // Decompress each partial signature and perform multi-scalar multiplication. + // result = sum( coeffs[i] * partials[i] ) in G2 + var result blst.P2 + for i, p := range partials { + sig, err := bls.SignatureFromBytes(p.Sig) + if err != nil { + return nil, fmt.Errorf("%w at index %d: %w", errInvalidPartialSig, p.Index, err) + } + + // Convert affine to projective, multiply by Lagrange coefficient, accumulate. + var proj blst.P2 + proj.FromAffine(sig) + scaled := proj.Mult(&coeffs[i]) + + if i == 0 { + result = *scaled + } else { + result.AddAssign(scaled) + } + } + + return result.ToAffine().Compress(), nil +} + +// Verify checks a reconstructed threshold signature against the group public key. +func (s *Scheme) Verify(sig []byte, msg []byte) error { + parsedSig, err := bls.SignatureFromBytes(sig) + if err != nil { + return fmt.Errorf("invalid threshold signature: %w", err) + } + + if !bls.Verify(s.GroupPK, parsedSig, msg) { + return errors.New("threshold signature verification failed") + } + + return nil +} diff --git a/utils/crypto/bls/threshold/scheme_test.go b/utils/crypto/bls/threshold/scheme_test.go new file mode 100644 index 000000000000..ec6ae9533679 --- /dev/null +++ b/utils/crypto/bls/threshold/scheme_test.go @@ -0,0 +1,211 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package threshold + +import ( + "testing" + + "github.com/ava-labs/avalanchego/utils/crypto/bls" + + "github.com/stretchr/testify/require" +) + +var testSeed = [32]byte{ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, +} + +var testMsg = []byte("test message for threshold BLS") + +func generateAndSign(t *testing.T, n, threshold int, msg []byte) (*Scheme, []PartialSig) { + t.Helper() + + scheme, signers, _, err := GenerateKeys(n, threshold, testSeed) + require.NoError(t, err) + + partials := make([]PartialSig, n) + for i, signer := range signers { + sig, err := signer.Sign(msg) + require.NoError(t, err) + partials[i] = PartialSig{ + Index: i + 1, // 1-based + Sig: bls.SignatureToBytes(sig), + } + } + + return scheme, partials +} + +// TestReconstructMatchesMasterKey verifies that the reconstructed threshold +// signature is byte-identical to a signature produced directly by the master key. +func TestReconstructMatchesMasterKey(t *testing.T) { + scheme, signers, masterSK, err := GenerateKeys(4, 3, testSeed) + require.NoError(t, err) + + // Sign directly with the master key. + directSig := new(bls.Signature).Sign(masterSK, testMsg, bls.CiphersuiteSignature.Bytes()) + directSigBytes := bls.SignatureToBytes(directSig) + + // Sign with 3 shares and reconstruct. + partials := make([]PartialSig, 3) + for i := 0; i < 3; i++ { + sig, err := signers[i].Sign(testMsg) + require.NoError(t, err) + partials[i] = PartialSig{ + Index: i + 1, + Sig: bls.SignatureToBytes(sig), + } + } + + reconstructed, err := scheme.Reconstruct(partials) + require.NoError(t, err) + + // Must be byte-identical. + require.Equal(t, directSigBytes, reconstructed) +} + +// TestReconstructHappyPath verifies basic reconstruction with exactly t shares. +func TestReconstructHappyPath(t *testing.T) { + scheme, partials := generateAndSign(t, 4, 3, testMsg) + + // Use first 3 of 4 partials. + reconstructed, err := scheme.Reconstruct(partials[:3]) + require.NoError(t, err) + require.Len(t, reconstructed, 96) // compressed G2 point + + // Verify against group PK. + err = scheme.Verify(reconstructed, testMsg) + require.NoError(t, err) +} + +// TestReconstructDeterministic verifies that different subsets of t shares +// produce the same threshold signature. +func TestReconstructDeterministic(t *testing.T) { + scheme, partials := generateAndSign(t, 4, 3, testMsg) + + // Subset 1: indices {1, 2, 3} + sig1, err := scheme.Reconstruct(partials[:3]) + require.NoError(t, err) + + // Subset 2: indices {1, 2, 4} + sig2, err := scheme.Reconstruct([]PartialSig{partials[0], partials[1], partials[3]}) + require.NoError(t, err) + + // Subset 3: indices {1, 3, 4} + sig3, err := scheme.Reconstruct([]PartialSig{partials[0], partials[2], partials[3]}) + require.NoError(t, err) + + // Subset 4: indices {2, 3, 4} + sig4, err := scheme.Reconstruct([]PartialSig{partials[1], partials[2], partials[3]}) + require.NoError(t, err) + + // All subsets must produce the same signature. + require.Equal(t, sig1, sig2) + require.Equal(t, sig1, sig3) + require.Equal(t, sig1, sig4) +} + +// TestReconstructInsufficientShares verifies that fewer than t shares fail. +func TestReconstructInsufficientShares(t *testing.T) { + scheme, partials := generateAndSign(t, 4, 3, testMsg) + + // Only 2 of 3 required. + _, err := scheme.Reconstruct(partials[:2]) + require.ErrorIs(t, err, errInsufficientPartials) +} + +// TestReconstructInvalidPartial verifies that a corrupted partial signature +// returns an error. +func TestReconstructInvalidPartial(t *testing.T) { + scheme, partials := generateAndSign(t, 4, 3, testMsg) + + // Corrupt one partial. + corrupted := make([]PartialSig, 3) + copy(corrupted, partials[:3]) + corrupted[1].Sig = make([]byte, 96) + corrupted[1].Sig[0] = 0xFF // invalid G2 point + + _, err := scheme.Reconstruct(corrupted) + require.Error(t, err) + require.ErrorIs(t, err, errInvalidPartialSig) +} + +// TestReconstructDuplicateIndex verifies that duplicate signer indices fail. +func TestReconstructDuplicateIndex(t *testing.T) { + scheme, partials := generateAndSign(t, 4, 3, testMsg) + + duplicated := []PartialSig{ + partials[0], + partials[0], // same index twice + partials[2], + } + + _, err := scheme.Reconstruct(duplicated) + require.Error(t, err) + require.ErrorIs(t, err, errDuplicateIndex) +} + +// TestVerifyAgainstGroupPK verifies that a reconstructed signature passes +// verification against the group public key. +func TestVerifyAgainstGroupPK(t *testing.T) { + scheme, partials := generateAndSign(t, 4, 3, testMsg) + + sig, err := scheme.Reconstruct(partials[:3]) + require.NoError(t, err) + + err = scheme.Verify(sig, testMsg) + require.NoError(t, err) +} + +// TestVerifyRejectsWrongMessage verifies that a reconstructed signature fails +// verification against a different message. +func TestVerifyRejectsWrongMessage(t *testing.T) { + scheme, partials := generateAndSign(t, 4, 3, testMsg) + + sig, err := scheme.Reconstruct(partials[:3]) + require.NoError(t, err) + + err = scheme.Verify(sig, []byte("wrong message")) + require.Error(t, err) +} + +// TestHasEnough verifies the quorum check. +func TestHasEnough(t *testing.T) { + scheme := NewScheme(3, 4, nil) + + require.False(t, scheme.HasEnough(0)) + require.False(t, scheme.HasEnough(1)) + require.False(t, scheme.HasEnough(2)) + require.True(t, scheme.HasEnough(3)) + require.True(t, scheme.HasEnough(4)) +} + +// TestRemaining verifies the remaining count. +func TestRemaining(t *testing.T) { + scheme := NewScheme(3, 4, nil) + + require.Equal(t, 3, scheme.Remaining(0)) + require.Equal(t, 2, scheme.Remaining(1)) + require.Equal(t, 1, scheme.Remaining(2)) + require.Equal(t, 0, scheme.Remaining(3)) + require.Equal(t, 0, scheme.Remaining(4)) +} + +// TestReconstructMoreThanThreshold verifies that providing more than t shares +// still works correctly. +func TestReconstructMoreThanThreshold(t *testing.T) { + scheme, partials := generateAndSign(t, 4, 3, testMsg) + + // Use all 4 partials (more than threshold of 3). + sig, err := scheme.Reconstruct(partials) + require.NoError(t, err) + + err = scheme.Verify(sig, testMsg) + require.NoError(t, err) + + // Should produce the same result as using exactly 3. + sig3, err := scheme.Reconstruct(partials[:3]) + require.NoError(t, err) + require.Equal(t, sig3, sig) +} diff --git a/utils/crypto/bls/threshold/threshold_test.go b/utils/crypto/bls/threshold/threshold_test.go new file mode 100644 index 000000000000..dd6919b6b3a0 --- /dev/null +++ b/utils/crypto/bls/threshold/threshold_test.go @@ -0,0 +1,44 @@ +// Copyright (C) 2019, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package threshold + +import ( + "testing" + + "github.com/ava-labs/avalanchego/utils/crypto/bls" + + "github.com/stretchr/testify/require" +) + +// TestGenerateKeysDeterministic verifies that the same seed produces the +// same keys and group PK. +func TestGenerateKeysDeterministic(t *testing.T) { + scheme1, signers1, master1, err := GenerateKeys(4, 3, testSeed) + require.NoError(t, err) + + scheme2, signers2, master2, err := GenerateKeys(4, 3, testSeed) + require.NoError(t, err) + + // Same group PK. + require.Equal(t, bls.PublicKeyToCompressedBytes(scheme1.GroupPK), bls.PublicKeyToCompressedBytes(scheme2.GroupPK)) + + // Same master SK. + require.Equal(t, master1.Serialize(), master2.Serialize()) + + // Same signer public keys. + for i := range signers1 { + pk1 := bls.PublicKeyToCompressedBytes(signers1[i].PublicKey()) + pk2 := bls.PublicKeyToCompressedBytes(signers2[i].PublicKey()) + require.Equal(t, pk1, pk2) + } +} + +// TestGenerateKeysInvalidParams verifies that invalid parameters are rejected. +func TestGenerateKeysInvalidParams(t *testing.T) { + _, _, _, err := GenerateKeys(4, 0, testSeed) + require.Error(t, err) + + _, _, _, err = GenerateKeys(2, 3, testSeed) + require.Error(t, err) +} diff --git a/vms/example/xsvm/builder/BUILD.bazel b/vms/example/xsvm/builder/BUILD.bazel index 991cf63ecefc..cd0f5597bae6 100644 --- a/vms/example/xsvm/builder/BUILD.bazel +++ b/vms/example/xsvm/builder/BUILD.bazel @@ -17,5 +17,6 @@ go_library( "//vms/example/xsvm/chain", "//vms/example/xsvm/execute", "//vms/example/xsvm/tx", + "@org_uber_go_zap//:zap", ], ) diff --git a/vms/example/xsvm/builder/builder.go b/vms/example/xsvm/builder/builder.go index f7829d3f0a6e..c6729a19688a 100644 --- a/vms/example/xsvm/builder/builder.go +++ b/vms/example/xsvm/builder/builder.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/avalanchego/vms/example/xsvm/chain" "github.com/ava-labs/avalanchego/vms/example/xsvm/execute" "github.com/ava-labs/avalanchego/vms/example/xsvm/tx" + "go.uber.org/zap" smblock "github.com/ava-labs/avalanchego/snow/engine/snowman/block" xsblock "github.com/ava-labs/avalanchego/vms/example/xsvm/block" @@ -90,6 +91,7 @@ func (b *builder) BuildBlock(ctx context.Context, blockContext *smblock.Context) if err != nil { return nil, err } + b.chainContext.Log.Debug("preffered block", zap.Stringer("blockID", preferredBlk.ID()), zap.Stringer("Parent block id", preferredBlk.Parent())) preferredState, err := preferredBlk.State() if err != nil {