diff --git a/integrations/openclaw/SKILL.md b/integrations/openclaw/SKILL.md index 3c58c26..bc41b9f 100644 --- a/integrations/openclaw/SKILL.md +++ b/integrations/openclaw/SKILL.md @@ -1,12 +1,12 @@ --- name: boltzpay -description: Pay for API data automatically — multi-protocol (x402 + L402), multi-chain -metadata: {"openclaw": {"emoji": "\u26a1", "requires": {"bins": ["npx"]}, "install": [{"id": "boltzpay-cli", "kind": "node", "label": "BoltzPay CLI"}]}} +description: Pay for API data automatically — multi-protocol (x402 + L402 + MPP), multi-chain +metadata: {"openclaw": {"emoji": "\u26a1", "requires": {"bins": ["npx"], "env": ["COINBASE_API_KEY_ID", "COINBASE_API_KEY_SECRET", "COINBASE_WALLET_SECRET"]}, "install": [{"id": "boltzpay-cli", "kind": "node", "label": "BoltzPay CLI"}]}} --- # BoltzPay — Paid API Access for AI Agents -BoltzPay lets AI agents pay for API data automatically. It supports multiple payment protocols (x402 and L402) and multiple blockchains (Base and Solana), paying with USDC or Bitcoin Lightning. Agents can discover, evaluate, and purchase API data in a single workflow. +BoltzPay lets AI agents pay for API data automatically. It supports multiple payment protocols (x402, L402, and MPP) and multiple blockchains (Base, Solana, and Tempo), paying with USDC, Bitcoin Lightning, or Stripe. Agents can discover, evaluate, and purchase API data in a single workflow. ## Quick Start diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 37f6d0a..e42730f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,6 +23,7 @@ export { } from "./shared/payment-errors"; export type { EndpointInputHints, + MppMethodQuote, ProtocolAdapter, ProtocolQuote, ProtocolResult, diff --git a/packages/core/src/shared/protocol-adapter.ts b/packages/core/src/shared/protocol-adapter.ts index 31ac547..1a23bfb 100644 --- a/packages/core/src/shared/protocol-adapter.ts +++ b/packages/core/src/shared/protocol-adapter.ts @@ -9,6 +9,15 @@ export interface EndpointInputHints { readonly outputExample?: unknown; } +export interface MppMethodQuote { + readonly method: string; + readonly intent: string; + readonly amount: Money; + readonly currency: string; + readonly network: string | undefined; + readonly recipient: string | undefined; +} + export interface ProtocolQuote { readonly amount: Money; readonly protocol: string; @@ -17,6 +26,9 @@ export interface ProtocolQuote { readonly scheme: string; readonly allAccepts?: readonly AcceptOption[]; readonly inputHints?: EndpointInputHints; + readonly allMethods?: readonly MppMethodQuote[]; + readonly selectedMethod?: string; + readonly priceUnknown?: boolean; } export interface ProtocolResult { diff --git a/packages/protocols/package.json b/packages/protocols/package.json index 76a1977..ff45114 100644 --- a/packages/protocols/package.json +++ b/packages/protocols/package.json @@ -58,17 +58,18 @@ "dependencies": { "@boltzpay/core": "workspace:*", "@coinbase/cdp-sdk": "^1.44.1", + "@getalby/sdk": "^7.0.0", "@solana/kit": "^6.1.0", "@x402/core": "^2.6.0", "@x402/evm": "^2.6.0", "@x402/fetch": "^2.6.0", "@x402/svm": "^2.6.0", "async-mutex": "^0.5.0", - "@getalby/sdk": "^7.0.0", "light-bolt11-decoder": "^3.2.0" }, "devDependencies": { "@boltzpay/config": "workspace:*", + "mppx": "0.4.7", "tsup": "^8.5.1", "typescript": "^5.8.3", "vitest": "^4.0.18" diff --git a/packages/protocols/src/adapter-error.ts b/packages/protocols/src/adapter-error.ts index 9462740..a06f931 100644 --- a/packages/protocols/src/adapter-error.ts +++ b/packages/protocols/src/adapter-error.ts @@ -70,6 +70,18 @@ export class L402CredentialsMissingError extends AdapterError { } } +export class MppQuoteError extends AdapterError { + constructor(message: string) { + super("mpp_quote_failed", message); + } +} + +export class MppPaymentError extends AdapterError { + constructor(message: string, options?: ErrorOptions) { + super("mpp_payment_failed", message, options); + } +} + /** * Thrown when all payment adapters fail during fallback execution. * Contains all individual errors for diagnostic context. diff --git a/packages/protocols/src/index.ts b/packages/protocols/src/index.ts index 22b8b2f..b46b348 100644 --- a/packages/protocols/src/index.ts +++ b/packages/protocols/src/index.ts @@ -1,4 +1,14 @@ export type { DeliveryAttemptResult } from "./adapter-error"; +export { hasMppScheme, parseMppChallenges } from "./mpp/mpp-parsing"; +export { MppMethodSelector } from "./mpp/mpp-method-selector"; +export type { MppResolvedMethod } from "./mpp/mpp-method-selector"; +export { MppAdapter } from "./mpp/mpp-adapter"; +export { buildMppQuote } from "./mpp/mpp-quote-builder"; +export type { + MppChallenge, + MppParseResult, + MppRequest, +} from "./mpp/mpp-types"; export { AdapterError, AggregatePaymentError, @@ -6,6 +16,8 @@ export { L402CredentialsMissingError, L402PaymentError, L402QuoteError, + MppPaymentError, + MppQuoteError, X402PaymentError, X402QuoteError, } from "./adapter-error"; diff --git a/packages/protocols/src/mpp/mpp-adapter.ts b/packages/protocols/src/mpp/mpp-adapter.ts new file mode 100644 index 0000000..7329020 --- /dev/null +++ b/packages/protocols/src/mpp/mpp-adapter.ts @@ -0,0 +1,130 @@ +import type { + ProtocolAdapter, + ProtocolQuote, + ProtocolResult, +} from "@boltzpay/core"; +import { MppPaymentError, MppQuoteError } from "../adapter-error"; +import type { ResponseAwareAdapter } from "../router/protocol-router"; +import type { AdapterTimeouts } from "../x402/x402-adapter"; +import type { MppMethodSelector } from "./mpp-method-selector"; +import { hasMppScheme, parseMppChallenges } from "./mpp-parsing"; +import { buildMppQuote } from "./mpp-quote-builder"; + +const DEFAULT_DETECT_MS = 10_000; +const DEFAULT_QUOTE_MS = 15_000; +const DEFAULT_PAYMENT_MS = 30_000; +const HTTP_PAYMENT_REQUIRED = 402; + +export class MppAdapter implements ResponseAwareAdapter { + readonly name = "mpp"; + private readonly timeouts: Required; + + constructor( + private readonly methodSelector: MppMethodSelector, + private readonly validateUrl: (url: string) => void, + timeouts?: AdapterTimeouts, + ) { + this.timeouts = { + detect: timeouts?.detect ?? DEFAULT_DETECT_MS, + quote: timeouts?.quote ?? DEFAULT_QUOTE_MS, + payment: timeouts?.payment ?? DEFAULT_PAYMENT_MS, + }; + } + + async detect( + url: string, + _headers?: Record, + ): Promise { + this.validateUrl(url); + let response: Response; + try { + response = await fetch(url, { + method: "GET", + redirect: "manual", + signal: AbortSignal.timeout(this.timeouts.detect), + }); + } catch (err) { + const msg = err instanceof Error ? err.message : "Network error"; + throw new MppQuoteError(`Cannot reach endpoint: ${msg}`); + } + if (response.status !== HTTP_PAYMENT_REQUIRED) { + return false; + } + const wwwAuth = response.headers.get("www-authenticate"); + if (!wwwAuth) { + return false; + } + return hasMppScheme(wwwAuth); + } + + async quote( + url: string, + _headers?: Record, + ): Promise { + this.validateUrl(url); + const response = await this.fetchForQuote(url); + return this.extractQuote(response); + } + + async quoteFromResponse(response: Response): Promise { + if (response.status !== HTTP_PAYMENT_REQUIRED) { + return null; + } + const wwwAuth = response.headers.get("www-authenticate"); + if (!wwwAuth || !hasMppScheme(wwwAuth)) { + return null; + } + try { + const { challenges } = parseMppChallenges(wwwAuth); + return buildMppQuote(challenges, this.methodSelector); + } catch { + return null; + } + } + + async execute(_request: { + readonly url: string; + readonly method: string; + readonly headers: Record; + readonly body: Uint8Array | undefined; + readonly amount: import("@boltzpay/core").Money; + }): Promise { + throw new MppPaymentError( + "MPP execute() not implemented -- see Phase 18", + ); + } + + private async fetchForQuote(url: string): Promise { + let response: Response; + try { + response = await fetch(url, { + method: "GET", + redirect: "error", + signal: AbortSignal.timeout(this.timeouts.quote), + }); + } catch (err) { + const msg = err instanceof Error ? err.message : "Network error"; + throw new MppQuoteError(`Cannot reach endpoint: ${msg}`); + } + if (response.status !== HTTP_PAYMENT_REQUIRED) { + throw new MppQuoteError( + `Expected 402 status, got ${response.status}`, + ); + } + return response; + } + + private extractQuote(response: Response): ProtocolQuote { + const wwwAuth = response.headers.get("www-authenticate"); + if (!wwwAuth || !hasMppScheme(wwwAuth)) { + throw new MppQuoteError( + "No MPP payment information in 402 response", + ); + } + const { challenges } = parseMppChallenges(wwwAuth); + if (challenges.length === 0) { + throw new MppQuoteError("No MPP challenges found in response"); + } + return buildMppQuote(challenges, this.methodSelector); + } +} diff --git a/packages/protocols/src/mpp/mpp-method-selector.ts b/packages/protocols/src/mpp/mpp-method-selector.ts new file mode 100644 index 0000000..29ed774 --- /dev/null +++ b/packages/protocols/src/mpp/mpp-method-selector.ts @@ -0,0 +1,102 @@ +import type { Money } from "@boltzpay/core"; + +export interface MppResolvedMethod { + readonly method: string; + readonly intent: string; + readonly amount: Money; + readonly currency: string; + readonly network: string | undefined; + readonly recipient: string | undefined; +} + +const WALLET_TO_MPP_METHOD: Readonly> = { + nwc: ["lightning"], + "stripe-mpp": ["stripe"], + tempo: ["tempo"], + "visa-mpp": ["card"], +} as const; + +function buildMethodToWalletMap(): ReadonlyMap { + const reverse = new Map(); + for (const [walletType, methods] of Object.entries(WALLET_TO_MPP_METHOD)) { + for (const m of methods) { + const existing = reverse.get(m) ?? []; + existing.push(walletType); + reverse.set(m, existing); + } + } + return reverse; +} + +const METHOD_TO_WALLET = buildMethodToWalletMap(); + +function sortByCheapest( + a: MppResolvedMethod, + b: MppResolvedMethod, +): number { + const aZero = a.amount.isZero(); + const bZero = b.amount.isZero(); + if (aZero && !bZero) return 1; + if (!aZero && bZero) return -1; + if (a.amount.cents < b.amount.cents) return -1; + if (a.amount.cents > b.amount.cents) return 1; + return 0; +} + +export class MppMethodSelector { + private readonly configuredWalletTypes: ReadonlySet; + private readonly preferredMethods: readonly string[]; + + constructor( + configuredWalletTypes: ReadonlySet, + preferredMethods: readonly string[], + ) { + this.configuredWalletTypes = configuredWalletTypes; + this.preferredMethods = preferredMethods; + } + + select(methods: readonly MppResolvedMethod[]): MppResolvedMethod { + if (this.preferredMethods.length > 0) { + return this.selectByPreference(methods); + } + + if (this.configuredWalletTypes.size > 0) { + return this.selectByWallet(methods); + } + + return this.selectCheapest(methods); + } + + private selectByPreference( + methods: readonly MppResolvedMethod[], + ): MppResolvedMethod { + for (const preferred of this.preferredMethods) { + const match = methods.find((m) => m.method === preferred); + if (match) return match; + } + return this.selectCheapest(methods); + } + + private selectByWallet( + methods: readonly MppResolvedMethod[], + ): MppResolvedMethod { + const compatible = methods.filter((m) => this.isWalletCompatible(m.method)); + if (compatible.length === 0) { + return this.selectCheapest(methods); + } + return this.selectCheapest(compatible); + } + + private isWalletCompatible(methodName: string): boolean { + const walletTypes = METHOD_TO_WALLET.get(methodName); + if (!walletTypes) return false; + return walletTypes.some((wt) => this.configuredWalletTypes.has(wt)); + } + + private selectCheapest( + methods: readonly MppResolvedMethod[], + ): MppResolvedMethod { + const sorted = [...methods].sort(sortByCheapest); + return sorted[0]!; + } +} diff --git a/packages/protocols/src/mpp/mpp-parsing.ts b/packages/protocols/src/mpp/mpp-parsing.ts new file mode 100644 index 0000000..580dde3 --- /dev/null +++ b/packages/protocols/src/mpp/mpp-parsing.ts @@ -0,0 +1,132 @@ +import type { MppChallenge, MppParseResult, MppRequest } from "./mpp-types"; + +const MPP_SCHEME_RE = /(?:^|,\s*)Payment\s+\w+\s*=/i; +const PARAM_RE = /(\w+)\s*=\s*(?:"([^"]*)"|([^\s,]+))/g; + +function hasMppScheme(wwwAuthenticate: string): boolean { + return MPP_SCHEME_RE.test(wwwAuthenticate); +} + +function parseMppChallenges(wwwAuthenticate: string): MppParseResult { + if (!hasMppScheme(wwwAuthenticate)) { + return { challenges: [] }; + } + const sections = splitChallenges(wwwAuthenticate); + const challenges: MppChallenge[] = []; + for (const section of sections) { + const challenge = parseSingleChallenge(section); + if (challenge) challenges.push(challenge); + } + return { challenges }; +} + +function splitChallenges(header: string): string[] { + const starts: number[] = []; + let i = 0; + while (i < header.length) { + if (header[i] === '"') { + i++; + while (i < header.length && header[i] !== '"') i++; + i++; + continue; + } + const remaining = header.slice(i); + const m = remaining.match(/^Payment\s/i); + if (m && (i === 0 || /[\s,]/.test(header[i - 1]!))) { + starts.push(i + m[0].length); + } + i++; + } + const results: string[] = []; + for (let j = 0; j < starts.length; j++) { + const start = starts[j]; + if (start === undefined) continue; + const end = starts[j + 1]; + const raw = + end !== undefined ? header.slice(start, end) : header.slice(start); + results.push(raw.replace(/,\s*Payment\s*$/i, "").replace(/,\s*$/, "").trim()); + } + return results; +} + +function parseMppParams(content: string): Record { + const params: Record = {}; + for (const match of content.matchAll(PARAM_RE)) { + const key = match[1]; + const value = match[2] ?? match[3]; + if (key !== undefined && value !== undefined) { + params[key.toLowerCase()] = value; + } + } + return params; +} + +function parseSingleChallenge(content: string): MppChallenge | undefined { + const params = parseMppParams(content); + const method = params.method; + if (!method) return undefined; + return { + id: params.id, + method, + intent: params.intent ?? "charge", + realm: params.realm, + expires: params.expires, + request: params.request ? decodeMppRequest(params.request) : undefined, + }; +} + +function decodeBase64Url(encoded: string): string { + const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/"); + const padLength = (4 - (base64.length % 4)) % 4; + return atob(base64 + "=".repeat(padLength)); +} + +function decodeMppRequest(encoded: string): MppRequest | undefined { + try { + const json = decodeBase64Url(encoded); + const parsed: unknown = JSON.parse(json); + return validateMppRequest(parsed); + } catch { + // Intent: malformed request payload should not crash diagnosis + return undefined; + } +} + +function validateMppRequest(data: unknown): MppRequest | undefined { + if (typeof data !== "object" || data === null) return undefined; + // External protocol data — narrowing after runtime typeof+null check + const obj = data as Record; + const amount = typeof obj.amount === "string" ? obj.amount : undefined; + const currency = typeof obj.currency === "string" ? obj.currency : undefined; + const recipient = + typeof obj.recipient === "string" ? obj.recipient : undefined; + if (!amount || !currency || !recipient) return undefined; + const details = extractMethodDetails(obj.methodDetails); + return { + amount, + currency, + recipient, + chainId: details?.chainId, + methodDetails: details?.raw, + }; +} + +function extractMethodDetails( + raw: unknown, +): { chainId: number | undefined; raw: Record } | undefined { + if (typeof raw !== "object" || raw === null) return undefined; + // External protocol data — narrowing after runtime typeof+null check + const obj = raw as Record; + const chainId = typeof obj.chainId === "number" ? obj.chainId : undefined; + return { chainId, raw: obj }; +} + +export { + decodeBase64Url, + decodeMppRequest, + hasMppScheme, + parseMppChallenges, + parseMppParams, + parseSingleChallenge, + splitChallenges, +}; diff --git a/packages/protocols/src/mpp/mpp-quote-builder.ts b/packages/protocols/src/mpp/mpp-quote-builder.ts new file mode 100644 index 0000000..fa8bbe7 --- /dev/null +++ b/packages/protocols/src/mpp/mpp-quote-builder.ts @@ -0,0 +1,100 @@ +import { Money, type MppMethodQuote, type ProtocolQuote } from "@boltzpay/core"; +import { MppQuoteError } from "../adapter-error"; +import { usdcAtomicToCents } from "../x402/usdc-conversion"; +import type { MppMethodSelector, MppResolvedMethod } from "./mpp-method-selector"; +import type { MppChallenge } from "./mpp-types"; + +function challengeToMethod(challenge: MppChallenge): MppResolvedMethod { + if (!challenge.request) { + return { + method: challenge.method, + intent: challenge.intent, + amount: Money.zero(), + currency: "unknown", + network: undefined, + recipient: undefined, + }; + } + + const rawAmount = BigInt(challenge.request.amount); + if (rawAmount < 0n) { + throw new MppQuoteError( + `Negative amount in MPP challenge: ${challenge.request.amount}`, + ); + } + + const amount = convertAmount( + rawAmount, + challenge.method, + challenge.request.chainId, + ); + + return { + method: challenge.method, + intent: challenge.intent, + amount, + currency: challenge.request.currency, + network: challenge.request.chainId?.toString(), + recipient: challenge.request.recipient, + }; +} + +function convertAmount( + rawAmount: bigint, + method: string, + chainId: number | undefined, +): Money { + if (method === "stripe") { + return Money.fromCents(rawAmount); + } + if (chainId !== undefined) { + return Money.fromCents(usdcAtomicToCents(rawAmount)); + } + if (method === "lightning") { + return Money.fromSatoshis(rawAmount); + } + return Money.fromCents(rawAmount); +} + +function toMethodQuote(resolved: MppResolvedMethod): MppMethodQuote { + return { + method: resolved.method, + intent: resolved.intent, + amount: resolved.amount, + currency: resolved.currency, + network: resolved.network, + recipient: resolved.recipient, + }; +} + +export function buildMppQuote( + challenges: readonly MppChallenge[], + selector: MppMethodSelector, +): ProtocolQuote { + const chargeChallenges = challenges.filter((c) => c.intent === "charge"); + + if (chargeChallenges.length === 0) { + throw new MppQuoteError( + "No charge challenges in MPP response -- session-only endpoints require Phase 18", + ); + } + + const methods = chargeChallenges.map(challengeToMethod); + const primary = selector.select(methods); + const allMethods = methods.map(toMethodQuote); + + const hasNoRequest = chargeChallenges.some((c) => !c.request); + const priceUnknown = + primary.amount.isZero() && hasNoRequest ? true : undefined; + + return { + amount: primary.amount, + protocol: "mpp", + network: primary.network, + payTo: primary.recipient, + scheme: "mpp", + allMethods: allMethods.length > 1 ? allMethods : undefined, + selectedMethod: primary.method, + priceUnknown, + }; +} diff --git a/packages/protocols/src/mpp/mpp-types.ts b/packages/protocols/src/mpp/mpp-types.ts new file mode 100644 index 0000000..73bbe8a --- /dev/null +++ b/packages/protocols/src/mpp/mpp-types.ts @@ -0,0 +1,22 @@ +interface MppRequest { + readonly amount: string; + readonly currency: string; + readonly recipient: string; + readonly chainId: number | undefined; + readonly methodDetails: Readonly> | undefined; +} + +interface MppChallenge { + readonly id: string | undefined; + readonly method: string; + readonly intent: string; + readonly realm: string | undefined; + readonly expires: string | undefined; + readonly request: MppRequest | undefined; +} + +interface MppParseResult { + readonly challenges: readonly MppChallenge[]; +} + +export type { MppChallenge, MppParseResult, MppRequest }; diff --git a/packages/protocols/src/x402/x402-parsing-helpers.ts b/packages/protocols/src/x402/x402-parsing-helpers.ts index f3afa92..a6adbaa 100644 --- a/packages/protocols/src/x402/x402-parsing-helpers.ts +++ b/packages/protocols/src/x402/x402-parsing-helpers.ts @@ -238,17 +238,17 @@ function parseWwwAuthenticate( if (x402Index === -1) return null; const content = headerValue.slice(x402Index + X402_SCHEME_PREFIX.length); const params: Record = {}; - for (const match of content.matchAll(/(\w+)="([^"]*)"/g)) { + for (const match of content.matchAll(/(\w+)\s*=\s*(?:"([^"]*)"|([^\s,]+))/g)) { const key = match[1]; - const value = match[2]; - if (key !== undefined && value !== undefined) params[key] = value; + const value = match[2] ?? match[3]; + if (key !== undefined && value !== undefined) params[key.toLowerCase()] = value; } const address = params.address; const rawAmount = params.amount; if (!address || !rawAmount) return null; const atomicAmount = usdcDisplayToAtomic(rawAmount); if (!atomicAmount) return null; - const chainId = params.chainId; + const chainId = params.chainid; const network = chainId ? `eip155:${chainId}` : DEFAULT_EIP155_NETWORK; const token = params.token ?? ""; return { diff --git a/packages/protocols/tests/adapter-error.test.ts b/packages/protocols/tests/adapter-error.test.ts index 297db57..cfee0ed 100644 --- a/packages/protocols/tests/adapter-error.test.ts +++ b/packages/protocols/tests/adapter-error.test.ts @@ -5,6 +5,8 @@ import { L402CredentialsMissingError, L402PaymentError, L402QuoteError, + MppPaymentError, + MppQuoteError, X402PaymentError, X402QuoteError, } from "../src/adapter-error"; @@ -48,6 +50,24 @@ describe("AdapterError subclasses", () => { expect(error.cause).toBe(originalError); }); + it('MppQuoteError should have code "mpp_quote_failed"', () => { + const error = new MppQuoteError("no charge challenges"); + expect(error.code).toBe("mpp_quote_failed"); + expect(error.message).toBe("no charge challenges"); + }); + + it('MppPaymentError should have code "mpp_payment_failed"', () => { + const error = new MppPaymentError("payment rejected"); + expect(error.code).toBe("mpp_payment_failed"); + expect(error.message).toBe("payment rejected"); + }); + + it("MppPaymentError should preserve cause via ErrorOptions", () => { + const cause = new Error("upstream failure"); + const error = new MppPaymentError("payment rejected", { cause }); + expect(error.cause).toBe(cause); + }); + it("All subclasses should be instanceof AdapterError", () => { const errors: AdapterError[] = [ new X402PaymentError("test"), @@ -56,6 +76,8 @@ describe("AdapterError subclasses", () => { new L402QuoteError("test"), new L402PaymentError("test"), new L402CredentialsMissingError(), + new MppQuoteError("test"), + new MppPaymentError("test"), ]; for (const error of errors) { @@ -72,6 +94,8 @@ describe("AdapterError subclasses", () => { new L402QuoteError("test"), new L402PaymentError("test"), new L402CredentialsMissingError(), + new MppQuoteError("test"), + new MppPaymentError("test"), ]; for (const error of errors) { diff --git a/packages/protocols/tests/mpp/learning/challenge.test.ts b/packages/protocols/tests/mpp/learning/challenge.test.ts new file mode 100644 index 0000000..6a70196 --- /dev/null +++ b/packages/protocols/tests/mpp/learning/challenge.test.ts @@ -0,0 +1,341 @@ +import { describe, expect, it } from "vitest"; +import { Challenge, Credential } from "mppx"; +import { Methods as TempoMethods } from "mppx/tempo"; +import { Methods as StripeMethods } from "mppx/stripe"; + +describe("mppx Challenge (learning tests)", () => { + const REALM = "api.example.com"; + const SECRET = "test-secret-key-for-hmac"; + + const tempoRequest = { + amount: "1000000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + recipient: "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + }; + + const stripeRequest = { + amount: "500", + currency: "usd", + decimals: 2, + networkId: "acct_abc123", + paymentMethodTypes: ["card"], + }; + + it("from() creates a challenge with explicit id", () => { + const challenge = Challenge.from({ + id: "test-id-123", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + + expect(challenge.id).toBe("test-id-123"); + expect(challenge.realm).toBe(REALM); + expect(challenge.method).toBe("tempo"); + expect(challenge.intent).toBe("charge"); + expect(challenge.request).toBeDefined(); + }); + + it("from() creates a challenge with HMAC-bound id via secretKey", () => { + const challenge = Challenge.from( + { + secretKey: SECRET, + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }, + ); + + expect(challenge.id).toBeDefined(); + expect(typeof challenge.id).toBe("string"); + expect(challenge.id.length).toBeGreaterThan(0); + expect(challenge.method).toBe("tempo"); + }); + + it("fromMethod() creates a validated challenge for tempo charge", () => { + const challenge = Challenge.fromMethod(TempoMethods.charge, { + secretKey: SECRET, + realm: REALM, + request: tempoRequest, + }); + + expect(challenge.method).toBe("tempo"); + expect(challenge.intent).toBe("charge"); + expect(challenge.realm).toBe(REALM); + }); + + it("fromMethod() creates a validated challenge for stripe charge", () => { + const challenge = Challenge.fromMethod(StripeMethods.charge, { + secretKey: SECRET, + realm: REALM, + request: stripeRequest, + }); + + expect(challenge.method).toBe("stripe"); + expect(challenge.intent).toBe("charge"); + }); + + it("serialize() produces a WWW-Authenticate header value starting with 'Payment '", () => { + const challenge = Challenge.from({ + id: "serial-test", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + + const header = Challenge.serialize(challenge); + + expect(typeof header).toBe("string"); + expect(header.startsWith("Payment ")).toBe(true); + }); + + it("deserialize() round-trips: serialize then deserialize produces equivalent challenge", () => { + const original = Challenge.from({ + id: "roundtrip-test", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + + const serialized = Challenge.serialize(original); + const deserialized = Challenge.deserialize(serialized); + + expect(deserialized.id).toBe(original.id); + expect(deserialized.realm).toBe(original.realm); + expect(deserialized.method).toBe(original.method); + expect(deserialized.intent).toBe(original.intent); + }); + + it("deserializeList() handles multiple challenges from a merged header", () => { + const tempoChallenge = Challenge.from({ + id: "tempo-1", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + + const stripeChallenge = Challenge.from({ + id: "stripe-1", + realm: REALM, + method: "stripe", + intent: "charge", + request: stripeRequest, + }); + + const mergedHeader = [ + Challenge.serialize(tempoChallenge), + Challenge.serialize(stripeChallenge), + ].join(", "); + + const challenges = Challenge.deserializeList(mergedHeader); + + expect(challenges).toHaveLength(2); + expect(challenges[0].method).toBe("tempo"); + expect(challenges[1].method).toBe("stripe"); + }); + + it("fromResponse() extracts challenge from a 402 Response", () => { + const challenge = Challenge.from({ + id: "from-response", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + + const header = Challenge.serialize(challenge); + const response = new Response(null, { + status: 402, + headers: { "WWW-Authenticate": header }, + }); + + const extracted = Challenge.fromResponse(response); + + expect(extracted.id).toBe("from-response"); + expect(extracted.method).toBe("tempo"); + expect(extracted.intent).toBe("charge"); + }); + + it("fromResponseList() extracts all challenges from a multi-challenge Response", () => { + const tempo = Challenge.from({ + id: "list-tempo", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + const stripe = Challenge.from({ + id: "list-stripe", + realm: REALM, + method: "stripe", + intent: "charge", + request: stripeRequest, + }); + + const merged = [Challenge.serialize(tempo), Challenge.serialize(stripe)].join(", "); + const response = new Response(null, { + status: 402, + headers: { "WWW-Authenticate": merged }, + }); + + const extracted = Challenge.fromResponseList(response); + + expect(extracted.length).toBeGreaterThanOrEqual(2); + const methods = extracted.map((c) => c.method); + expect(methods).toContain("tempo"); + expect(methods).toContain("stripe"); + }); + + it("fromHeaders() extracts challenge from Headers object", () => { + const challenge = Challenge.from({ + id: "from-headers", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + + const header = Challenge.serialize(challenge); + const headers = new Headers({ "WWW-Authenticate": header }); + + const extracted = Challenge.fromHeaders(headers); + + expect(extracted.id).toBe("from-headers"); + expect(extracted.method).toBe("tempo"); + }); + + it("fromHeadersList() extracts list from Headers object", () => { + const tempo = Challenge.from({ + id: "hlist-tempo", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + const stripe = Challenge.from({ + id: "hlist-stripe", + realm: REALM, + method: "stripe", + intent: "charge", + request: stripeRequest, + }); + + const merged = [Challenge.serialize(tempo), Challenge.serialize(stripe)].join(", "); + const headers = new Headers({ "WWW-Authenticate": merged }); + + const extracted = Challenge.fromHeadersList(headers); + + expect(extracted.length).toBeGreaterThanOrEqual(2); + }); + + it("verify() with secretKey validates HMAC-bound challenge ID", () => { + const challenge = Challenge.from( + { + secretKey: SECRET, + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }, + ); + + const isValid = Challenge.verify(challenge, { secretKey: SECRET }); + expect(isValid).toBe(true); + }); + + it("verify() rejects tampered challenge", () => { + const challenge = Challenge.from( + { + secretKey: SECRET, + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }, + ); + + const tampered = { ...challenge, realm: "hacked.example.com" }; + const isValid = Challenge.verify(tampered, { secretKey: SECRET }); + expect(isValid).toBe(false); + }); + + it("meta() extracts opaque metadata from challenge", () => { + const challenge = Challenge.from({ + id: "meta-test", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + meta: { orderId: "order-123", userId: "user-456" }, + }); + + const metadata = Challenge.meta(challenge); + + expect(metadata).toBeDefined(); + expect(metadata).toEqual({ orderId: "order-123", userId: "user-456" }); + }); + + it("Schema validates correct challenge shape", () => { + const validData = { + id: "schema-test", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }; + + const result = Challenge.Schema.safeParse(validData); + expect(result.success).toBe(true); + }); + + it("Schema rejects invalid challenge (missing required fields)", () => { + const invalidData = { + id: "invalid", + realm: REALM, + }; + + const result = Challenge.Schema.safeParse(invalidData); + expect(result.success).toBe(false); + }); + + it("challenge with description preserves it through serialization", () => { + const challenge = Challenge.from({ + id: "desc-test", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + description: "Access to premium API", + }); + + expect(challenge.description).toBe("Access to premium API"); + + const serialized = Challenge.serialize(challenge); + const deserialized = Challenge.deserialize(serialized); + expect(deserialized.description).toBe("Access to premium API"); + }); + + it("challenge with expires preserves it through serialization", () => { + const expires = "2026-12-31T23:59:59Z"; + const challenge = Challenge.from({ + id: "exp-test", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + expires, + }); + + expect(challenge.expires).toBe(expires); + + const serialized = Challenge.serialize(challenge); + const deserialized = Challenge.deserialize(serialized); + expect(deserialized.expires).toBe(expires); + }); +}); diff --git a/packages/protocols/tests/mpp/learning/credential.test.ts b/packages/protocols/tests/mpp/learning/credential.test.ts new file mode 100644 index 0000000..e061a93 --- /dev/null +++ b/packages/protocols/tests/mpp/learning/credential.test.ts @@ -0,0 +1,129 @@ +import { describe, expect, it } from "vitest"; +import { Challenge, Credential } from "mppx"; +import { Methods as TempoMethods } from "mppx/tempo"; + +describe("mppx Credential (learning tests)", () => { + const REALM = "api.example.com"; + const SECRET = "test-secret-key"; + + const tempoRequest = { + amount: "1000000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + recipient: "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + }; + + function createTestChallenge() { + return Challenge.from({ + id: "cred-test-challenge", + realm: REALM, + method: "tempo", + intent: "charge", + request: tempoRequest, + }); + } + + it("from() creates credential from challenge + payload", () => { + const challenge = createTestChallenge(); + const credential = Credential.from({ + challenge, + payload: { signature: "0xdeadbeef", type: "transaction" }, + }); + + expect(credential.challenge).toBeDefined(); + expect(credential.challenge.id).toBe("cred-test-challenge"); + expect(credential.payload).toEqual({ signature: "0xdeadbeef", type: "transaction" }); + }); + + it("from() creates credential with optional source (DID)", () => { + const challenge = createTestChallenge(); + const credential = Credential.from({ + challenge, + payload: { signature: "0xdeadbeef", type: "transaction" }, + source: "did:pkh:eip155:42431:0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + }); + + expect(credential.source).toBe( + "did:pkh:eip155:42431:0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + ); + }); + + it("serialize() produces 'Payment base64url...' Authorization header value", () => { + const challenge = createTestChallenge(); + const credential = Credential.from({ + challenge, + payload: { signature: "0xbeef" }, + }); + + const header = Credential.serialize(credential); + + expect(typeof header).toBe("string"); + expect(header.startsWith("Payment ")).toBe(true); + expect(header.length).toBeGreaterThan("Payment ".length); + }); + + it("deserialize() round-trips correctly", () => { + const challenge = createTestChallenge(); + const original = Credential.from({ + challenge, + payload: { txHash: "0xabc123" }, + }); + + const serialized = Credential.serialize(original); + const deserialized = Credential.deserialize(serialized); + + expect(deserialized.challenge.id).toBe(original.challenge.id); + expect(deserialized.challenge.method).toBe("tempo"); + expect(deserialized.payload).toEqual(original.payload); + }); + + it("fromRequest() extracts from Request's Authorization header", () => { + const challenge = createTestChallenge(); + const credential = Credential.from({ + challenge, + payload: { txHash: "0x123" }, + }); + + const header = Credential.serialize(credential); + const request = new Request("https://api.example.com/resource", { + headers: { Authorization: header }, + }); + + const extracted = Credential.fromRequest(request); + + expect(extracted.challenge.id).toBe("cred-test-challenge"); + expect(extracted.payload).toEqual({ txHash: "0x123" }); + }); + + it("extractPaymentScheme() extracts Payment scheme from header", () => { + const challenge = createTestChallenge(); + const credential = Credential.from({ + challenge, + payload: { data: "test" }, + }); + + const paymentHeader = Credential.serialize(credential); + const extracted = Credential.extractPaymentScheme(paymentHeader); + + expect(extracted).toBeDefined(); + expect(extracted).not.toBeNull(); + expect(extracted!.startsWith("Payment ")).toBe(true); + }); + + it("extractPaymentScheme() returns null when no Payment scheme present", () => { + const bearerHeader = "Bearer eyJhbGciOiJSUzI1NiJ9.test"; + const extracted = Credential.extractPaymentScheme(bearerHeader); + + expect(extracted).toBeNull(); + }); + + it("credential contains challenge reference and payload", () => { + const challenge = createTestChallenge(); + const payload = { signature: "0xsig", nonce: "abc" }; + + const credential = Credential.from({ challenge, payload }); + + expect(credential.challenge).toStrictEqual(challenge); + expect(credential.payload).toStrictEqual(payload); + }); +}); diff --git a/packages/protocols/tests/mpp/learning/errors.test.ts b/packages/protocols/tests/mpp/learning/errors.test.ts new file mode 100644 index 0000000..8952a23 --- /dev/null +++ b/packages/protocols/tests/mpp/learning/errors.test.ts @@ -0,0 +1,148 @@ +import { describe, expect, it } from "vitest"; +import { Errors } from "mppx"; + +describe("mppx Errors (learning tests)", () => { + it("PaymentError is the base abstract error class", () => { + expect(Errors.PaymentError).toBeDefined(); + expect(Errors.PaymentError.prototype).toBeInstanceOf(Error); + }); + + it("MalformedCredentialError extends PaymentError", () => { + const error = new Errors.MalformedCredentialError({ reason: "bad base64" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error).toBeInstanceOf(Error); + expect(error.name).toBe("MalformedCredentialError"); + expect(error.status).toBe(402); + expect(error.message).toContain("bad base64"); + }); + + it("InvalidChallengeError extends PaymentError", () => { + const error = new Errors.InvalidChallengeError({ id: "abc", reason: "expired" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("InvalidChallengeError"); + expect(error.status).toBe(402); + }); + + it("VerificationFailedError extends PaymentError", () => { + const error = new Errors.VerificationFailedError({ reason: "invalid signature" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("VerificationFailedError"); + }); + + it("PaymentActionRequiredError extends PaymentError", () => { + const error = new Errors.PaymentActionRequiredError({ reason: "requires_action" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("PaymentActionRequiredError"); + }); + + it("PaymentExpiredError extends PaymentError", () => { + const error = new Errors.PaymentExpiredError({ expires: "2026-01-01T00:00:00Z" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("PaymentExpiredError"); + }); + + it("PaymentRequiredError extends PaymentError", () => { + const error = new Errors.PaymentRequiredError({ description: "Premium access" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("PaymentRequiredError"); + }); + + it("InvalidPayloadError extends PaymentError", () => { + const error = new Errors.InvalidPayloadError({ reason: "missing signature" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("InvalidPayloadError"); + }); + + it("BadRequestError extends PaymentError with status 400", () => { + const error = new Errors.BadRequestError({ reason: "malformed request" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("BadRequestError"); + expect(error.status).toBe(400); + }); + + it("PaymentInsufficientError extends PaymentError", () => { + const error = new Errors.PaymentInsufficientError({ reason: "expected 1000, received 500" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("PaymentInsufficientError"); + }); + + it("PaymentMethodUnsupportedError extends PaymentError with status 400", () => { + const error = new Errors.PaymentMethodUnsupportedError({ method: "bitcoin" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("PaymentMethodUnsupportedError"); + expect(error.status).toBe(400); + }); + + it("InsufficientBalanceError (session) extends PaymentError", () => { + const error = new Errors.InsufficientBalanceError({ reason: "channel depleted" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("InsufficientBalanceError"); + expect(error.status).toBe(402); + }); + + it("InvalidSignatureError (session) extends PaymentError", () => { + const error = new Errors.InvalidSignatureError({ reason: "bad sig" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("InvalidSignatureError"); + }); + + it("SignerMismatchError (session) extends PaymentError", () => { + const error = new Errors.SignerMismatchError({ reason: "wrong signer" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("SignerMismatchError"); + }); + + it("AmountExceedsDepositError (session) extends PaymentError", () => { + const error = new Errors.AmountExceedsDepositError({ reason: "over limit" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("AmountExceedsDepositError"); + }); + + it("DeltaTooSmallError (session) extends PaymentError", () => { + const error = new Errors.DeltaTooSmallError({ reason: "min delta not met" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("DeltaTooSmallError"); + }); + + it("ChannelNotFoundError (session) extends PaymentError with status 410", () => { + const error = new Errors.ChannelNotFoundError({ reason: "unknown channel" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("ChannelNotFoundError"); + expect(error.status).toBe(410); + }); + + it("ChannelClosedError (session) extends PaymentError with status 410", () => { + const error = new Errors.ChannelClosedError({ reason: "finalized" }); + + expect(error).toBeInstanceOf(Errors.PaymentError); + expect(error.name).toBe("ChannelClosedError"); + expect(error.status).toBe(410); + }); + + it("PaymentError.toProblemDetails() produces RFC 9457 format", () => { + const error = new Errors.MalformedCredentialError({ reason: "bad json" }); + const details = error.toProblemDetails("challenge-123"); + + expect(details.type).toBe("https://paymentauth.org/problems/malformed-credential"); + expect(details.title).toBe("Malformed Credential"); + expect(details.status).toBe(402); + expect(details.detail).toBeDefined(); + expect(details.challengeId).toBe("challenge-123"); + }); +}); diff --git a/packages/protocols/tests/mpp/learning/integration.test.ts b/packages/protocols/tests/mpp/learning/integration.test.ts new file mode 100644 index 0000000..2c75395 --- /dev/null +++ b/packages/protocols/tests/mpp/learning/integration.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it } from "vitest"; +import { Challenge } from "mppx"; + +// Integration learning tests for mppx with real endpoints. +// Skipped by default -- run manually: +// cd packages/protocols && pnpm vitest run tests/mpp/learning/integration.test.ts --reporter=verbose +describe("mppx Integration (learning tests)", () => { + describe.skip("real endpoint probe (@slow)", () => { + it("Browserbase returns 402 with WWW-Authenticate: Payment header", async () => { + const response = await fetch("https://api.browserbase.com/v1/sessions", { + method: "POST", + signal: AbortSignal.timeout(10_000), + }); + + expect(response.status).toBe(402); + + const wwwAuth = response.headers.get("www-authenticate"); + expect(wwwAuth).toBeDefined(); + expect(wwwAuth).not.toBeNull(); + expect(wwwAuth!.toLowerCase()).toContain("payment"); + }); + + it("Challenge.fromResponse() succeeds on real Browserbase 402", async () => { + const response = await fetch("https://api.browserbase.com/v1/sessions", { + method: "POST", + signal: AbortSignal.timeout(10_000), + }); + + expect(response.status).toBe(402); + + const challenge = Challenge.fromResponse(response); + + expect(challenge.method).toBeDefined(); + expect(challenge.intent).toBeDefined(); + expect(challenge.realm).toBeDefined(); + expect(challenge.id).toBeDefined(); + }); + }); +}); diff --git a/packages/protocols/tests/mpp/learning/method.test.ts b/packages/protocols/tests/mpp/learning/method.test.ts new file mode 100644 index 0000000..b5778c3 --- /dev/null +++ b/packages/protocols/tests/mpp/learning/method.test.ts @@ -0,0 +1,126 @@ +import { describe, expect, it } from "vitest"; +import { Method } from "mppx"; +import { Methods as TempoMethods } from "mppx/tempo"; +import { Methods as StripeMethods } from "mppx/stripe"; + +describe("mppx Method (learning tests)", () => { + it("from() creates a method definition with name, intent, and schemas", () => { + const method = Method.from({ + name: "test-method", + intent: "charge", + schema: { + credential: { + payload: { _zod: { type: "string" } } as never, + }, + request: { _zod: { type: "string" } } as never, + }, + }); + + expect(method.name).toBe("test-method"); + expect(method.intent).toBe("charge"); + expect(method.schema).toBeDefined(); + expect(method.schema.credential).toBeDefined(); + expect(method.schema.request).toBeDefined(); + }); + + it("toClient() wraps a method with createCredential callback", () => { + const client = Method.toClient(TempoMethods.charge, { + async createCredential({ challenge }) { + return `Payment mock-credential-for-${challenge.id}`; + }, + }); + + expect(client.name).toBe("tempo"); + expect(client.intent).toBe("charge"); + expect(typeof client.createCredential).toBe("function"); + }); + + it("TempoMethods.charge has name='tempo' and intent='charge'", () => { + expect(TempoMethods.charge.name).toBe("tempo"); + expect(TempoMethods.charge.intent).toBe("charge"); + expect(TempoMethods.charge.schema).toBeDefined(); + expect(TempoMethods.charge.schema.request).toBeDefined(); + expect(TempoMethods.charge.schema.credential).toBeDefined(); + }); + + it("TempoMethods.charge request schema validates valid data", () => { + const validRequest = { + amount: "1000000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + recipient: "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + }; + + const result = TempoMethods.charge.schema.request.safeParse(validRequest); + expect(result.success).toBe(true); + }); + + it("TempoMethods.session has name='tempo' and intent='session'", () => { + expect(TempoMethods.session.name).toBe("tempo"); + expect(TempoMethods.session.intent).toBe("session"); + expect(TempoMethods.session.schema).toBeDefined(); + expect(TempoMethods.session.schema.request).toBeDefined(); + }); + + it("TempoMethods.session request schema validates valid data", () => { + const validRequest = { + amount: "1000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + unitType: "token", + }; + + const result = TempoMethods.session.schema.request.safeParse(validRequest); + expect(result.success).toBe(true); + }); + + it("StripeMethods.charge has name='stripe' and intent='charge'", () => { + expect(StripeMethods.charge.name).toBe("stripe"); + expect(StripeMethods.charge.intent).toBe("charge"); + expect(StripeMethods.charge.schema).toBeDefined(); + }); + + it("StripeMethods.charge request schema validates valid data", () => { + const validRequest = { + amount: "500", + currency: "usd", + decimals: 2, + networkId: "acct_abc123", + paymentMethodTypes: ["card"], + }; + + const result = StripeMethods.charge.schema.request.safeParse(validRequest); + expect(result.success).toBe(true); + }); + + it("tempo charge request schema rejects missing required fields", () => { + const invalidRequest = { + amount: "1000", + }; + + const result = TempoMethods.charge.schema.request.safeParse(invalidRequest); + expect(result.success).toBe(false); + }); + + it("stripe charge request schema rejects missing required fields", () => { + const invalidRequest = { + amount: "500", + currency: "usd", + }; + + const result = StripeMethods.charge.schema.request.safeParse(invalidRequest); + expect(result.success).toBe(false); + }); + + it("method names match expected string values (regression guard)", () => { + expect(TempoMethods.charge.name).toBe("tempo"); + expect(TempoMethods.session.name).toBe("tempo"); + expect(StripeMethods.charge.name).toBe("stripe"); + }); + + it("method intents match expected string values (regression guard)", () => { + expect(TempoMethods.charge.intent).toBe("charge"); + expect(TempoMethods.session.intent).toBe("session"); + expect(StripeMethods.charge.intent).toBe("charge"); + }); +}); diff --git a/packages/protocols/tests/mpp/learning/payment-request.test.ts b/packages/protocols/tests/mpp/learning/payment-request.test.ts new file mode 100644 index 0000000..3cb6a97 --- /dev/null +++ b/packages/protocols/tests/mpp/learning/payment-request.test.ts @@ -0,0 +1,109 @@ +import { describe, expect, it } from "vitest"; +import { PaymentRequest } from "mppx"; +import { Methods as TempoMethods } from "mppx/tempo"; +import { Methods as StripeMethods } from "mppx/stripe"; + +describe("mppx PaymentRequest (learning tests)", () => { + it("from() creates request with arbitrary fields", () => { + const request = PaymentRequest.from({ + amount: "1000000", + currency: "0x20c0000000000000000000000000000000000001", + recipient: "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + }); + + expect(request.amount).toBe("1000000"); + expect(request.currency).toBe("0x20c0000000000000000000000000000000000001"); + expect(request.recipient).toBe("0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00"); + }); + + it("fromMethod() validates and transforms tempo charge request (amount * 10^decimals)", () => { + // mppx fromMethod() applies a Zod transform pipeline that scales + // the amount by 10^decimals to produce atomic units + const request = PaymentRequest.fromMethod(TempoMethods.charge, { + amount: "1000000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + recipient: "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + }); + + // amount is scaled: "1000000" * 10^6 = "1000000000000" + expect(request.amount).toBe("1000000000000"); + expect(request.currency).toBe("0x20c0000000000000000000000000000000000001"); + }); + + it("fromMethod() validates and transforms stripe charge request (amount * 10^decimals)", () => { + // Stripe: "500" with decimals 2 => "50000" (500 * 100) + const request = PaymentRequest.fromMethod(StripeMethods.charge, { + amount: "500", + currency: "usd", + decimals: 2, + networkId: "acct_abc123", + paymentMethodTypes: ["card"], + }); + + expect(request.amount).toBe("50000"); + expect(request.currency).toBe("usd"); + }); + + it("serialize() encodes to base64url string", () => { + const request = PaymentRequest.from({ + amount: "1000", + currency: "usd", + }); + + const serialized = PaymentRequest.serialize(request); + + expect(typeof serialized).toBe("string"); + expect(serialized.length).toBeGreaterThan(0); + }); + + it("deserialize() decodes from base64url and restores fields", () => { + const serialized = PaymentRequest.serialize( + PaymentRequest.from({ + amount: "2500", + currency: "usd", + recipient: "acct_test", + }), + ); + + const deserialized = PaymentRequest.deserialize(serialized); + + expect(deserialized.amount).toBe("2500"); + expect(deserialized.currency).toBe("usd"); + expect(deserialized.recipient).toBe("acct_test"); + }); + + it("round-trip: serialize -> deserialize produces identical request", () => { + const original = PaymentRequest.from({ + amount: "999", + currency: "eur", + description: "test payment", + }); + + const roundTripped = PaymentRequest.deserialize( + PaymentRequest.serialize(original), + ); + + expect(roundTripped).toEqual(original); + }); + + it("fromMethod() rejects invalid tempo charge request (missing decimals)", () => { + expect(() => + PaymentRequest.fromMethod(TempoMethods.charge, { + amount: "1000", + currency: "0x20c", + } as never), + ).toThrow(); + }); + + it("fromMethod() rejects invalid stripe charge request (missing networkId)", () => { + expect(() => + PaymentRequest.fromMethod(StripeMethods.charge, { + amount: "500", + currency: "usd", + decimals: 2, + paymentMethodTypes: ["card"], + } as never), + ).toThrow(); + }); +}); diff --git a/packages/protocols/tests/mpp/learning/receipt.test.ts b/packages/protocols/tests/mpp/learning/receipt.test.ts new file mode 100644 index 0000000..8e857e7 --- /dev/null +++ b/packages/protocols/tests/mpp/learning/receipt.test.ts @@ -0,0 +1,122 @@ +import { describe, expect, it } from "vitest"; +import { Receipt } from "mppx"; + +describe("mppx Receipt (learning tests)", () => { + const TIMESTAMP = "2026-03-19T12:00:00Z"; + + it("from() creates receipt with method, status, timestamp, reference", () => { + const receipt = Receipt.from({ + method: "tempo", + status: "success", + timestamp: TIMESTAMP, + reference: "0xabc123def456", + }); + + expect(receipt.method).toBe("tempo"); + expect(receipt.status).toBe("success"); + expect(receipt.timestamp).toBe(TIMESTAMP); + expect(receipt.reference).toBe("0xabc123def456"); + }); + + it("from() creates receipt with optional externalId", () => { + const receipt = Receipt.from({ + method: "stripe", + status: "success", + timestamp: TIMESTAMP, + reference: "pi_test_123", + externalId: "order-456", + }); + + expect(receipt.externalId).toBe("order-456"); + }); + + it("Schema validates correct receipt shape", () => { + const validData = { + method: "tempo", + status: "success", + timestamp: TIMESTAMP, + reference: "0xabc", + }; + + const result = Receipt.Schema.safeParse(validData); + expect(result.success).toBe(true); + }); + + it("Schema rejects invalid status (must be 'success')", () => { + const invalidData = { + method: "tempo", + status: "failed", + timestamp: TIMESTAMP, + reference: "0xabc", + }; + + const result = Receipt.Schema.safeParse(invalidData); + expect(result.success).toBe(false); + }); + + it("serialize() encodes to base64url for Payment-Receipt header", () => { + const receipt = Receipt.from({ + method: "tempo", + status: "success", + timestamp: TIMESTAMP, + reference: "0xdeadbeef", + }); + + const serialized = Receipt.serialize(receipt); + + expect(typeof serialized).toBe("string"); + expect(serialized.length).toBeGreaterThan(0); + }); + + it("deserialize() decodes and restores all fields", () => { + const original = Receipt.from({ + method: "stripe", + status: "success", + timestamp: TIMESTAMP, + reference: "pi_abc", + }); + + const serialized = Receipt.serialize(original); + const deserialized = Receipt.deserialize(serialized); + + expect(deserialized.method).toBe("stripe"); + expect(deserialized.status).toBe("success"); + expect(deserialized.timestamp).toBe(TIMESTAMP); + expect(deserialized.reference).toBe("pi_abc"); + }); + + it("fromResponse() extracts receipt from Response Payment-Receipt header", () => { + const receipt = Receipt.from({ + method: "tempo", + status: "success", + timestamp: TIMESTAMP, + reference: "0x123", + }); + + const serialized = Receipt.serialize(receipt); + const response = new Response("OK", { + status: 200, + headers: { "Payment-Receipt": serialized }, + }); + + const extracted = Receipt.fromResponse(response); + + expect(extracted.method).toBe("tempo"); + expect(extracted.status).toBe("success"); + expect(extracted.reference).toBe("0x123"); + }); + + it("round-trip: from -> serialize -> deserialize produces equivalent receipt", () => { + const original = Receipt.from({ + method: "tempo", + status: "success", + timestamp: TIMESTAMP, + reference: "0xfull-roundtrip", + externalId: "ext-id-789", + }); + + const roundTripped = Receipt.deserialize(Receipt.serialize(original)); + + expect(roundTripped).toEqual(original); + }); +}); diff --git a/packages/protocols/tests/mpp/learning/session.test.ts b/packages/protocols/tests/mpp/learning/session.test.ts new file mode 100644 index 0000000..f47b180 --- /dev/null +++ b/packages/protocols/tests/mpp/learning/session.test.ts @@ -0,0 +1,178 @@ +import { describe, expect, it } from "vitest"; +import { Challenge, Errors } from "mppx"; +import { Methods as TempoMethods } from "mppx/tempo"; + +describe("mppx Session (learning tests)", () => { + const REALM = "api.example.com"; + const SECRET = "session-test-secret"; + // channelId requires 0x + 64 hex chars (32 bytes hash) + const CHANNEL_ID = "0x" + "ab".repeat(32); + const VALID_SIGNATURE = "0x" + "de".repeat(32); + const VALID_TX_HASH = "0x" + "cf".repeat(32); + + it("TempoMethods.session schema validates session request fields", () => { + const validRequest = { + amount: "5000000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + unitType: "token", + }; + + const result = TempoMethods.session.schema.request.safeParse(validRequest); + expect(result.success).toBe(true); + }); + + it("TempoMethods.session schema validates with optional fields", () => { + const fullRequest = { + amount: "5000000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + unitType: "token", + channelId: CHANNEL_ID, + escrowContract: "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + minVoucherDelta: "100000", + suggestedDeposit: "10000000", + recipient: "0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00", + }; + + const result = TempoMethods.session.schema.request.safeParse(fullRequest); + expect(result.success).toBe(true); + }); + + it("TempoMethods.session schema rejects missing required unitType", () => { + const invalidRequest = { + amount: "5000000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + }; + + const result = TempoMethods.session.schema.request.safeParse(invalidRequest); + expect(result.success).toBe(false); + }); + + it("session challenge can be created via Challenge.fromMethod", () => { + const challenge = Challenge.fromMethod(TempoMethods.session, { + secretKey: SECRET, + realm: REALM, + request: { + amount: "5000000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + unitType: "token", + }, + }); + + expect(challenge.method).toBe("tempo"); + expect(challenge.intent).toBe("session"); + expect(challenge.realm).toBe(REALM); + expect(challenge.id).toBeDefined(); + }); + + it("session challenge has intent='session' (not 'charge')", () => { + const sessionChallenge = Challenge.fromMethod(TempoMethods.session, { + id: "session-intent-test", + realm: REALM, + request: { + amount: "1000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + unitType: "token", + }, + }); + + const chargeChallenge = Challenge.fromMethod(TempoMethods.charge, { + id: "charge-intent-test", + realm: REALM, + request: { + amount: "1000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + }, + }); + + expect(sessionChallenge.intent).toBe("session"); + expect(chargeChallenge.intent).toBe("charge"); + expect(sessionChallenge.intent).not.toBe(chargeChallenge.intent); + }); + + it("session-specific error types are importable and instantiable", () => { + const insufficientBalance = new Errors.InsufficientBalanceError({ reason: "empty" }); + const channelNotFound = new Errors.ChannelNotFoundError({ reason: "no channel" }); + const channelClosed = new Errors.ChannelClosedError({ reason: "finalized" }); + const signerMismatch = new Errors.SignerMismatchError({ reason: "wrong key" }); + const amountExceeds = new Errors.AmountExceedsDepositError({ reason: "over deposit" }); + const deltaTooSmall = new Errors.DeltaTooSmallError({ reason: "min not met" }); + + expect(insufficientBalance).toBeInstanceOf(Errors.PaymentError); + expect(channelNotFound).toBeInstanceOf(Errors.PaymentError); + expect(channelClosed).toBeInstanceOf(Errors.PaymentError); + expect(signerMismatch).toBeInstanceOf(Errors.PaymentError); + expect(amountExceeds).toBeInstanceOf(Errors.PaymentError); + expect(deltaTooSmall).toBeInstanceOf(Errors.PaymentError); + }); + + it("session challenge serializes and deserializes correctly", () => { + const challenge = Challenge.fromMethod(TempoMethods.session, { + id: "session-serde", + realm: REALM, + request: { + amount: "2000", + currency: "0x20c0000000000000000000000000000000000001", + decimals: 6, + unitType: "token", + }, + }); + + const serialized = Challenge.serialize(challenge); + const deserialized = Challenge.deserialize(serialized); + + expect(deserialized.method).toBe("tempo"); + expect(deserialized.intent).toBe("session"); + expect(deserialized.id).toBe("session-serde"); + }); + + // Session lifecycle documentation: + // 1. Server issues session challenge (intent="session") + // 2. Client creates credential with action="open" + deposit transaction + // 3. Server verifies deposit, opens payment channel + // 4. Client sends action="topUp" credentials to add funds + // 5. Client sends action="voucher" credentials (cumulative amounts) for streaming payments + // 6. Client sends action="close" credential to finalize the channel + it("session credential payload schema validates open action", () => { + const openPayload = { + action: "open", + channelId: CHANNEL_ID, + cumulativeAmount: "0", + signature: VALID_SIGNATURE, + transaction: VALID_TX_HASH, + type: "transaction", + }; + + const result = TempoMethods.session.schema.credential.payload.safeParse(openPayload); + expect(result.success).toBe(true); + }); + + it("session credential payload schema validates voucher action", () => { + const voucherPayload = { + action: "voucher", + channelId: CHANNEL_ID, + cumulativeAmount: "500000", + signature: VALID_SIGNATURE, + }; + + const result = TempoMethods.session.schema.credential.payload.safeParse(voucherPayload); + expect(result.success).toBe(true); + }); + + it("session credential payload schema validates close action", () => { + const closePayload = { + action: "close", + channelId: CHANNEL_ID, + cumulativeAmount: "1000000", + signature: VALID_SIGNATURE, + }; + + const result = TempoMethods.session.schema.credential.payload.safeParse(closePayload); + expect(result.success).toBe(true); + }); +}); diff --git a/packages/protocols/tests/mpp/mpp-adapter.test.ts b/packages/protocols/tests/mpp/mpp-adapter.test.ts new file mode 100644 index 0000000..aa7a533 --- /dev/null +++ b/packages/protocols/tests/mpp/mpp-adapter.test.ts @@ -0,0 +1,300 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { MppPaymentError, MppQuoteError } from "../../src/adapter-error"; +import { MppMethodSelector } from "../../src/mpp/mpp-method-selector"; +import { MppAdapter } from "../../src/mpp/mpp-adapter"; + +function encodeBase64Url(obj: Record): string { + const json = JSON.stringify(obj); + const base64 = btoa(json); + return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); +} + +function buildMppHeader(methods: readonly { method: string; intent?: string; request?: Record }[]): string { + return methods.map((m) => { + const parts = [`method=${m.method}`, `intent=${m.intent ?? "charge"}`]; + if (m.request) { + parts.push(`request=${encodeBase64Url(m.request)}`); + } + return `Payment ${parts.join(" ")}`; + }).join(", "); +} + +function mockResponse(status: number, headers?: Record): Response { + return new Response(null, { status, headers }); +} + +function mockMppResponse(methods: readonly { method: string; intent?: string; request?: Record }[]): Response { + const header = buildMppHeader(methods); + return mockResponse(402, { "www-authenticate": header }); +} + +const stripeRequest = { + amount: "500", + currency: "USD", + recipient: "acct_123", +}; + +const tempoRequest = { + amount: "1000000", + currency: "USDC", + recipient: "0xabc", + methodDetails: { chainId: 42161 }, +}; + +function defaultAdapter(): MppAdapter { + const selector = new MppMethodSelector(new Set(), []); + const validateUrl = () => {}; + return new MppAdapter(selector, validateUrl); +} + +function adapterWithValidation(validateUrl: (url: string) => void): MppAdapter { + const selector = new MppMethodSelector(new Set(), []); + return new MppAdapter(selector, validateUrl); +} + +let originalFetch: typeof globalThis.fetch; + +beforeEach(() => { + originalFetch = globalThis.fetch; +}); + +afterEach(() => { + globalThis.fetch = originalFetch; + vi.restoreAllMocks(); +}); + +describe("MppAdapter", () => { + it("has name 'mpp'", () => { + const adapter = defaultAdapter(); + expect(adapter.name).toBe("mpp"); + }); + + describe("detect()", () => { + it("returns true for 402 with MPP Payment header", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockMppResponse([{ method: "tempo", request: tempoRequest }]), + ); + const adapter = defaultAdapter(); + const result = await adapter.detect("https://api.example.com/resource"); + expect(result).toBe(true); + }); + + it("returns false for 402 without MPP header", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockResponse(402), + ); + const adapter = defaultAdapter(); + const result = await adapter.detect("https://api.example.com/resource"); + expect(result).toBe(false); + }); + + it("returns false for 200 response", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockResponse(200), + ); + const adapter = defaultAdapter(); + const result = await adapter.detect("https://api.example.com/resource"); + expect(result).toBe(false); + }); + + it("returns false for 402 with Bearer header (not MPP)", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockResponse(402, { "www-authenticate": "Bearer realm=api" }), + ); + const adapter = defaultAdapter(); + const result = await adapter.detect("https://api.example.com/resource"); + expect(result).toBe(false); + }); + + it("throws MppQuoteError on network error", async () => { + globalThis.fetch = vi.fn().mockRejectedValue(new Error("ECONNREFUSED")); + const adapter = defaultAdapter(); + await expect( + adapter.detect("https://api.example.com/resource"), + ).rejects.toThrow(MppQuoteError); + }); + + it("calls validateUrl before fetching", async () => { + const validateUrl = vi.fn(); + const adapter = adapterWithValidation(validateUrl); + globalThis.fetch = vi.fn().mockResolvedValue(mockResponse(200)); + await adapter.detect("https://api.example.com/resource"); + expect(validateUrl).toHaveBeenCalledWith("https://api.example.com/resource"); + expect(validateUrl).toHaveBeenCalledBefore(globalThis.fetch as ReturnType); + }); + + it("respects detect timeout", async () => { + globalThis.fetch = vi.fn().mockImplementation((_url: string, init?: RequestInit) => { + expect(init?.signal).toBeDefined(); + return Promise.resolve(mockResponse(200)); + }); + const selector = new MppMethodSelector(new Set(), []); + const adapter = new MppAdapter(selector, () => {}, { detect: 5_000 }); + await adapter.detect("https://api.example.com/resource"); + expect(globalThis.fetch).toHaveBeenCalledOnce(); + }); + }); + + describe("quote()", () => { + it("returns ProtocolQuote with protocol=mpp and selected method", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockMppResponse([{ method: "stripe", request: stripeRequest }]), + ); + const adapter = defaultAdapter(); + const quote = await adapter.quote("https://api.example.com/resource"); + expect(quote.protocol).toBe("mpp"); + expect(quote.scheme).toBe("mpp"); + expect(quote.selectedMethod).toBe("stripe"); + }); + + it("returns quote with correct Money for Stripe method (fromCents)", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockMppResponse([{ method: "stripe", request: stripeRequest }]), + ); + const adapter = defaultAdapter(); + const quote = await adapter.quote("https://api.example.com/resource"); + expect(quote.amount.cents).toBe(500n); + expect(quote.amount.currency).toBe("USD"); + }); + + it("returns quote with correct Money for Tempo/USDC method (usdcAtomicToCents)", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockMppResponse([{ method: "tempo", request: tempoRequest }]), + ); + const adapter = defaultAdapter(); + const quote = await adapter.quote("https://api.example.com/resource"); + // 1_000_000 atomic / 10_000 = 100 cents = $1.00 + expect(quote.amount.cents).toBe(100n); + }); + + it("returns allMethods when multiple methods available", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockMppResponse([ + { method: "tempo", request: tempoRequest }, + { method: "stripe", request: stripeRequest }, + ]), + ); + const adapter = defaultAdapter(); + const quote = await adapter.quote("https://api.example.com/resource"); + expect(quote.allMethods).toBeDefined(); + expect(quote.allMethods).toHaveLength(2); + }); + + it("returns priceUnknown=true when challenge has no request", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockMppResponse([{ method: "tempo" }]), + ); + const adapter = defaultAdapter(); + const quote = await adapter.quote("https://api.example.com/resource"); + expect(quote.priceUnknown).toBe(true); + expect(quote.amount.isZero()).toBe(true); + }); + + it("throws MppQuoteError for non-402 response", async () => { + globalThis.fetch = vi.fn().mockResolvedValue(mockResponse(200)); + const adapter = defaultAdapter(); + await expect( + adapter.quote("https://api.example.com/resource"), + ).rejects.toThrow(MppQuoteError); + }); + + it("throws MppQuoteError when no MPP header present", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockResponse(402, { "www-authenticate": "Bearer realm=api" }), + ); + const adapter = defaultAdapter(); + await expect( + adapter.quote("https://api.example.com/resource"), + ).rejects.toThrow(MppQuoteError); + }); + + it("throws MppQuoteError when no challenges parsed", async () => { + globalThis.fetch = vi.fn().mockResolvedValue( + mockResponse(402, { "www-authenticate": "Payment" }), + ); + const adapter = defaultAdapter(); + await expect( + adapter.quote("https://api.example.com/resource"), + ).rejects.toThrow(MppQuoteError); + }); + + it("calls validateUrl before fetching", async () => { + const validateUrl = vi.fn(); + const adapter = adapterWithValidation(validateUrl); + globalThis.fetch = vi.fn().mockResolvedValue( + mockMppResponse([{ method: "stripe", request: stripeRequest }]), + ); + await adapter.quote("https://api.example.com/resource"); + expect(validateUrl).toHaveBeenCalledWith("https://api.example.com/resource"); + expect(validateUrl).toHaveBeenCalledBefore(globalThis.fetch as ReturnType); + }); + }); + + describe("quoteFromResponse()", () => { + it("extracts quote from 402 response with MPP header", async () => { + const response = mockMppResponse([{ method: "stripe", request: stripeRequest }]); + const adapter = defaultAdapter(); + const quote = await adapter.quoteFromResponse(response); + expect(quote).not.toBeNull(); + expect(quote!.protocol).toBe("mpp"); + expect(quote!.selectedMethod).toBe("stripe"); + expect(quote!.amount.cents).toBe(500n); + }); + + it("returns null for non-402 response", async () => { + const response = mockResponse(200); + const adapter = defaultAdapter(); + const quote = await adapter.quoteFromResponse(response); + expect(quote).toBeNull(); + }); + + it("returns null for 402 without MPP header", async () => { + const response = mockResponse(402, { "www-authenticate": "Bearer realm=api" }); + const adapter = defaultAdapter(); + const quote = await adapter.quoteFromResponse(response); + expect(quote).toBeNull(); + }); + + it("returns null on parse error (tolerant)", async () => { + const response = mockResponse(402, { "www-authenticate": "Payment method=tempo request=!!!invalid!!!" }); + const adapter = defaultAdapter(); + const quote = await adapter.quoteFromResponse(response); + // Tolerant: should not throw, returns null or a quote with zero amount + expect(quote === null || quote.priceUnknown === true).toBe(true); + }); + + it("does not call fetch (no network request)", async () => { + const fetchSpy = vi.fn(); + globalThis.fetch = fetchSpy; + const response = mockMppResponse([{ method: "stripe", request: stripeRequest }]); + const adapter = defaultAdapter(); + await adapter.quoteFromResponse(response); + expect(fetchSpy).not.toHaveBeenCalled(); + }); + }); + + describe("execute()", () => { + it("throws MppPaymentError with Phase 18 message", async () => { + const adapter = defaultAdapter(); + await expect( + adapter.execute({ + url: "https://api.example.com/resource", + method: "GET", + headers: {}, + body: undefined, + amount: (await import("@boltzpay/core")).Money.fromCents(100n), + }), + ).rejects.toThrow(MppPaymentError); + + await expect( + adapter.execute({ + url: "https://api.example.com/resource", + method: "GET", + headers: {}, + body: undefined, + amount: (await import("@boltzpay/core")).Money.fromCents(100n), + }), + ).rejects.toThrow("Phase 18"); + }); + }); +}); diff --git a/packages/protocols/tests/mpp/mpp-method-selector.test.ts b/packages/protocols/tests/mpp/mpp-method-selector.test.ts new file mode 100644 index 0000000..8aa88ed --- /dev/null +++ b/packages/protocols/tests/mpp/mpp-method-selector.test.ts @@ -0,0 +1,173 @@ +import { Money } from "@boltzpay/core"; +import { describe, expect, it } from "vitest"; +import { + MppMethodSelector, + type MppResolvedMethod, +} from "../../src/mpp/mpp-method-selector"; + +function method( + name: string, + intent: string, + cents: bigint, + currency = "USD", +): MppResolvedMethod { + return { + method: name, + intent, + amount: Money.fromCents(cents), + currency, + network: undefined, + recipient: undefined, + }; +} + +function satMethod( + name: string, + sats: bigint, +): MppResolvedMethod { + return { + method: name, + intent: "charge", + amount: Money.fromSatoshis(sats), + currency: "BTC", + network: undefined, + recipient: undefined, + }; +} + +describe("MppMethodSelector", () => { + describe("wallet-first selection", () => { + it("filters methods by configured wallet types and returns cheapest", () => { + const selector = new MppMethodSelector( + new Set(["stripe-mpp", "tempo"]), + [], + ); + const methods = [ + method("stripe", "charge", 500n), + method("tempo", "charge", 100n), + method("lightning", "charge", 50n), + ]; + + const selected = selector.select(methods); + expect(selected.method).toBe("tempo"); + }); + + it("maps nwc wallet type to lightning method", () => { + const selector = new MppMethodSelector(new Set(["nwc"]), []); + const methods = [ + method("stripe", "charge", 500n), + method("lightning", "charge", 50n), + ]; + + const selected = selector.select(methods); + expect(selected.method).toBe("lightning"); + }); + + it("maps visa-mpp wallet type to card method", () => { + const selector = new MppMethodSelector(new Set(["visa-mpp"]), []); + const methods = [ + method("card", "charge", 500n), + method("stripe", "charge", 100n), + ]; + + const selected = selector.select(methods); + expect(selected.method).toBe("card"); + }); + }); + + describe("preferredMethods override", () => { + it("returns first matching preferred method regardless of price", () => { + const selector = new MppMethodSelector( + new Set(["stripe-mpp", "tempo"]), + ["stripe", "tempo"], + ); + const methods = [ + method("tempo", "charge", 100n), + method("stripe", "charge", 500n), + ]; + + const selected = selector.select(methods); + expect(selected.method).toBe("stripe"); + }); + + it("skips preferred methods not in available list", () => { + const selector = new MppMethodSelector( + new Set(["tempo"]), + ["lightning", "tempo"], + ); + const methods = [ + method("tempo", "charge", 100n), + method("stripe", "charge", 50n), + ]; + + const selected = selector.select(methods); + expect(selected.method).toBe("tempo"); + }); + }); + + describe("fallback to cheapest", () => { + it("returns cheapest overall when no wallets configured", () => { + const selector = new MppMethodSelector(new Set(), []); + const methods = [ + method("stripe", "charge", 500n), + method("tempo", "charge", 100n), + method("lightning", "charge", 200n), + ]; + + const selected = selector.select(methods); + expect(selected.method).toBe("tempo"); + }); + + it("returns cheapest when no compatible wallet methods found", () => { + const selector = new MppMethodSelector(new Set(["visa-mpp"]), []); + const methods = [ + method("stripe", "charge", 500n), + method("tempo", "charge", 100n), + ]; + + const selected = selector.select(methods); + expect(selected.method).toBe("tempo"); + }); + + it("sorts zero-amount methods last", () => { + const selector = new MppMethodSelector(new Set(), []); + const methods = [ + method("tempo", "charge", 0n), + method("stripe", "charge", 100n), + ]; + + const selected = selector.select(methods); + expect(selected.method).toBe("stripe"); + }); + + it("returns zero-amount method if all are zero", () => { + const selector = new MppMethodSelector(new Set(), []); + const methods = [ + method("tempo", "charge", 0n), + method("stripe", "charge", 0n), + ]; + + const selected = selector.select(methods); + expect(selected.amount.isZero()).toBe(true); + }); + }); + + describe("edge cases", () => { + it("returns the only method when list has one entry", () => { + const selector = new MppMethodSelector(new Set(), []); + const methods = [method("tempo", "charge", 100n)]; + + const selected = selector.select(methods); + expect(selected.method).toBe("tempo"); + }); + + it("handles sats-denominated methods in sorting", () => { + const selector = new MppMethodSelector(new Set(["nwc"]), []); + const methods = [satMethod("lightning", 1000n)]; + + const selected = selector.select(methods); + expect(selected.method).toBe("lightning"); + expect(selected.amount.cents).toBe(1000n); + }); + }); +}); diff --git a/packages/protocols/tests/mpp/mpp-parsing.test.ts b/packages/protocols/tests/mpp/mpp-parsing.test.ts new file mode 100644 index 0000000..8a12685 --- /dev/null +++ b/packages/protocols/tests/mpp/mpp-parsing.test.ts @@ -0,0 +1,366 @@ +import { describe, expect, it } from "vitest"; + +import { + decodeBase64Url, + decodeMppRequest, + hasMppScheme, + parseMppChallenges, + parseMppParams, + parseSingleChallenge, + splitChallenges, +} from "../../src/mpp/mpp-parsing"; + +function encodeBase64Url(obj: Record): string { + const json = JSON.stringify(obj); + return btoa(json).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); +} + +const TEMPO_REQUEST = { + amount: "10000", + currency: "0x20C0D54F37EF0E3B2A5E3a7C9Ab0bFe15f2F1b80", + recipient: "0x10409f8a084D05AbC4E12A8dD8d4CeDF41F06Ce2", + methodDetails: { chainId: 4217 }, +}; + +const STRIPE_REQUEST = { + amount: "1000", + currency: "usd", + recipient: "acct_1234567890", + methodDetails: { paymentMethodTypes: ["card"] }, +}; + +describe("hasMppScheme", () => { + it("detects Payment scheme with quoted params", () => { + expect(hasMppScheme('Payment id="abc", method="tempo"')).toBe(true); + }); + + it("detects Payment scheme with unquoted params", () => { + expect(hasMppScheme("Payment method=tempo")).toBe(true); + }); + + it("detects Payment scheme case-insensitively", () => { + expect(hasMppScheme('payment method="tempo"')).toBe(true); + expect(hasMppScheme('PAYMENT method="tempo"')).toBe(true); + }); + + it("rejects x402 scheme", () => { + expect(hasMppScheme('x402 address="0xabc", amount="0.01"')).toBe(false); + }); + + it("rejects L402 scheme", () => { + expect(hasMppScheme('L402 macaroon="abc", invoice="lnbc..."')).toBe(false); + }); + + it("rejects empty string", () => { + expect(hasMppScheme("")).toBe(false); + }); + + it("rejects Payment without params", () => { + expect(hasMppScheme("Payment")).toBe(false); + }); + + it("rejects Bearer with Payment in realm value", () => { + expect(hasMppScheme('Bearer realm="Payment Portal", scope=read')).toBe( + false, + ); + }); +}); + +describe("splitChallenges", () => { + it("splits single challenge", () => { + const result = splitChallenges('Payment id="a", method="tempo"'); + expect(result).toHaveLength(1); + expect(result[0]).toContain("method"); + }); + + it("splits two challenges", () => { + const header = + 'Payment id="a", method="tempo", intent="charge", ' + + 'Payment id="b", method="stripe", intent="charge"'; + const result = splitChallenges(header); + expect(result).toHaveLength(2); + expect(result[0]).toContain("tempo"); + expect(result[1]).toContain("stripe"); + }); + + it("handles extra whitespace between challenges", () => { + const header = + 'Payment method="tempo", Payment method="stripe"'; + const result = splitChallenges(header); + expect(result).toHaveLength(2); + }); + + it("does not split on Payment inside quoted value", () => { + const header = + 'Payment method="tempo", realm="Payment Gateway", ' + + 'Payment method="stripe", intent="charge"'; + const result = splitChallenges(header); + expect(result).toHaveLength(2); + expect(result[0]).toContain("tempo"); + expect(result[0]).toContain("Payment Gateway"); + expect(result[1]).toContain("stripe"); + }); + + it("handles Payment in base64 request value without splitting", () => { + const header = + 'Payment method="tempo", request="UGF5bWVudCBtZXRob2Q9", ' + + 'Payment method="stripe"'; + const result = splitChallenges(header); + expect(result).toHaveLength(2); + expect(result[0]).toContain("tempo"); + expect(result[1]).toContain("stripe"); + }); +}); + +describe("parseMppParams", () => { + it("parses quoted values", () => { + const result = parseMppParams('id="abc", method="tempo"'); + expect(result.id).toBe("abc"); + expect(result.method).toBe("tempo"); + }); + + it("parses unquoted values", () => { + const result = parseMppParams("method=tempo, intent=charge"); + expect(result.method).toBe("tempo"); + expect(result.intent).toBe("charge"); + }); + + it("normalizes keys to lowercase", () => { + const result = parseMppParams('Method="tempo", Intent="charge"'); + expect(result.method).toBe("tempo"); + expect(result.intent).toBe("charge"); + }); + + it("handles mixed quoted and unquoted", () => { + const result = parseMppParams('id="abc", method=tempo'); + expect(result.id).toBe("abc"); + expect(result.method).toBe("tempo"); + }); + + it("returns empty for empty string", () => { + expect(parseMppParams("")).toEqual({}); + }); +}); + +describe("parseSingleChallenge", () => { + it("parses challenge with all fields", () => { + const encoded = encodeBase64Url(TEMPO_REQUEST); + const content = + `id="x7Tg", method="tempo", intent="charge", ` + + `realm="api.example.com", expires="2025-01-15T12:05:00Z", request="${encoded}"`; + const result = parseSingleChallenge(content); + expect(result).toBeDefined(); + expect(result?.method).toBe("tempo"); + expect(result?.intent).toBe("charge"); + expect(result?.id).toBe("x7Tg"); + expect(result?.realm).toBe("api.example.com"); + expect(result?.expires).toBe("2025-01-15T12:05:00Z"); + expect(result?.request?.amount).toBe("10000"); + expect(result?.request?.chainId).toBe(4217); + }); + + it("defaults intent to charge when missing", () => { + const result = parseSingleChallenge('method="tempo"'); + expect(result?.intent).toBe("charge"); + }); + + it("returns undefined when method is missing", () => { + const result = parseSingleChallenge('id="abc", intent="charge"'); + expect(result).toBeUndefined(); + }); + + it("parses session intent", () => { + const result = parseSingleChallenge( + 'method="lightning", intent="session"', + ); + expect(result?.method).toBe("lightning"); + expect(result?.intent).toBe("session"); + }); + + it("parses challenge without request param", () => { + const result = parseSingleChallenge( + 'id="abc", method="stripe", intent="charge"', + ); + expect(result).toBeDefined(); + expect(result?.method).toBe("stripe"); + expect(result?.request).toBeUndefined(); + }); +}); + +describe("decodeBase64Url", () => { + it("decodes standard base64url", () => { + const original = '{"amount":"10000"}'; + const encoded = btoa(original) + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/, ""); + expect(decodeBase64Url(encoded)).toBe(original); + }); + + it("handles padding correctly", () => { + const original = "ab"; + const encoded = btoa(original).replace(/=+$/, ""); + expect(decodeBase64Url(encoded)).toBe(original); + }); + + it("handles regular base64 (with + and /)", () => { + const original = '{"key":"value with spaces"}'; + const encoded = btoa(original); + expect(decodeBase64Url(encoded)).toBe(original); + }); +}); + +describe("decodeMppRequest", () => { + it("decodes valid tempo request", () => { + const encoded = encodeBase64Url(TEMPO_REQUEST); + const result = decodeMppRequest(encoded); + expect(result).toBeDefined(); + expect(result?.amount).toBe("10000"); + expect(result?.currency).toBe(TEMPO_REQUEST.currency); + expect(result?.recipient).toBe(TEMPO_REQUEST.recipient); + expect(result?.chainId).toBe(4217); + expect(result?.methodDetails).toEqual({ chainId: 4217 }); + }); + + it("decodes valid stripe request", () => { + const encoded = encodeBase64Url(STRIPE_REQUEST); + const result = decodeMppRequest(encoded); + expect(result).toBeDefined(); + expect(result?.amount).toBe("1000"); + expect(result?.currency).toBe("usd"); + expect(result?.recipient).toBe("acct_1234567890"); + expect(result?.chainId).toBeUndefined(); + }); + + it("returns undefined for missing required fields", () => { + const encoded = encodeBase64Url({ amount: "100" }); + expect(decodeMppRequest(encoded)).toBeUndefined(); + }); + + it("returns undefined for invalid base64", () => { + expect(decodeMppRequest("!!!not-base64!!!")).toBeUndefined(); + }); + + it("returns undefined for non-JSON content", () => { + const encoded = btoa("this is not json"); + expect(decodeMppRequest(encoded)).toBeUndefined(); + }); + + it("returns undefined for non-object JSON", () => { + const encoded = btoa('"just a string"'); + expect(decodeMppRequest(encoded)).toBeUndefined(); + }); + + it("returns undefined for null JSON", () => { + const encoded = btoa("null"); + expect(decodeMppRequest(encoded)).toBeUndefined(); + }); + + it("handles request without methodDetails", () => { + const encoded = encodeBase64Url({ + amount: "500", + currency: "usd", + recipient: "acct_test", + }); + const result = decodeMppRequest(encoded); + expect(result).toBeDefined(); + expect(result?.chainId).toBeUndefined(); + expect(result?.methodDetails).toBeUndefined(); + }); +}); + +describe("parseMppChallenges", () => { + it("parses single tempo challenge", () => { + const encoded = encodeBase64Url(TEMPO_REQUEST); + const header = `Payment id="x7Tg", method="tempo", intent="charge", request="${encoded}"`; + const { challenges } = parseMppChallenges(header); + expect(challenges).toHaveLength(1); + expect(challenges[0]?.method).toBe("tempo"); + expect(challenges[0]?.request?.amount).toBe("10000"); + expect(challenges[0]?.request?.chainId).toBe(4217); + }); + + it("parses multi-method Browserbase-style header", () => { + const tempoReq = encodeBase64Url(TEMPO_REQUEST); + const stripeReq = encodeBase64Url(STRIPE_REQUEST); + const header = + `Payment id="a1", method="tempo", intent="charge", request="${tempoReq}", ` + + `Payment id="b2", method="stripe", intent="charge", request="${stripeReq}"`; + const { challenges } = parseMppChallenges(header); + expect(challenges).toHaveLength(2); + expect(challenges[0]?.method).toBe("tempo"); + expect(challenges[0]?.request?.chainId).toBe(4217); + expect(challenges[1]?.method).toBe("stripe"); + expect(challenges[1]?.request?.currency).toBe("usd"); + }); + + it("returns empty for non-MPP header", () => { + const { challenges } = parseMppChallenges( + 'x402 address="0xabc", amount="0.01"', + ); + expect(challenges).toHaveLength(0); + }); + + it("returns empty for empty string", () => { + const { challenges } = parseMppChallenges(""); + expect(challenges).toHaveLength(0); + }); + + it("skips challenges missing method param", () => { + const header = + 'Payment id="a1", intent="charge", ' + + 'Payment id="b2", method="stripe", intent="charge"'; + const { challenges } = parseMppChallenges(header); + expect(challenges).toHaveLength(1); + expect(challenges[0]?.method).toBe("stripe"); + }); + + it("handles session intent for lightning streaming", () => { + const header = 'Payment method="lightning", intent="session"'; + const { challenges } = parseMppChallenges(header); + expect(challenges).toHaveLength(1); + expect(challenges[0]?.intent).toBe("session"); + }); + + it("handles case-insensitive scheme", () => { + const header = 'payment method="tempo", intent="charge"'; + const { challenges } = parseMppChallenges(header); + expect(challenges).toHaveLength(1); + expect(challenges[0]?.method).toBe("tempo"); + }); + + it("handles malformed request gracefully", () => { + const header = 'Payment method="tempo", request="not-valid-base64-json"'; + const { challenges } = parseMppChallenges(header); + expect(challenges).toHaveLength(1); + expect(challenges[0]?.method).toBe("tempo"); + expect(challenges[0]?.request).toBeUndefined(); + }); + + it("parses four payment methods", () => { + const header = + 'Payment method="tempo", intent="charge", ' + + 'Payment method="stripe", intent="charge", ' + + 'Payment method="lightning", intent="session", ' + + 'Payment method="card", intent="charge"'; + const { challenges } = parseMppChallenges(header); + expect(challenges).toHaveLength(4); + expect(challenges.map((c) => c.method)).toEqual([ + "tempo", + "stripe", + "lightning", + "card", + ]); + }); + + it("preserves realm containing Payment keyword", () => { + const header = + 'Payment method="tempo", realm="Payment Gateway", ' + + 'Payment method="stripe", intent="charge"'; + const { challenges } = parseMppChallenges(header); + expect(challenges).toHaveLength(2); + expect(challenges[0]?.method).toBe("tempo"); + expect(challenges[0]?.realm).toBe("Payment Gateway"); + expect(challenges[1]?.method).toBe("stripe"); + }); +}); diff --git a/packages/protocols/tests/mpp/mpp-quote-builder.test.ts b/packages/protocols/tests/mpp/mpp-quote-builder.test.ts new file mode 100644 index 0000000..e0000a8 --- /dev/null +++ b/packages/protocols/tests/mpp/mpp-quote-builder.test.ts @@ -0,0 +1,196 @@ +import { Money } from "@boltzpay/core"; +import { describe, expect, it } from "vitest"; +import { MppMethodSelector } from "../../src/mpp/mpp-method-selector"; +import { buildMppQuote } from "../../src/mpp/mpp-quote-builder"; +import type { MppChallenge } from "../../src/mpp/mpp-types"; +import { MppQuoteError } from "../../src/adapter-error"; + +const tempoCharge: MppChallenge = { + id: "c1", + method: "tempo", + intent: "charge", + realm: undefined, + expires: undefined, + request: { + amount: "1000000", + currency: "USDC", + recipient: "0xabc", + chainId: 42161, + methodDetails: undefined, + }, +}; + +const stripeCharge: MppChallenge = { + id: "c2", + method: "stripe", + intent: "charge", + realm: undefined, + expires: undefined, + request: { + amount: "500", + currency: "USD", + recipient: "acct_123", + chainId: undefined, + methodDetails: undefined, + }, +}; + +const lightningCharge: MppChallenge = { + id: "c3", + method: "lightning", + intent: "charge", + realm: undefined, + expires: undefined, + request: { + amount: "1000", + currency: "BTC", + recipient: "lnbc1...", + chainId: undefined, + methodDetails: undefined, + }, +}; + +const sessionOnly: MppChallenge = { + id: "c4", + method: "tempo", + intent: "session", + realm: undefined, + expires: undefined, + request: { + amount: "0", + currency: "USDC", + recipient: "0xabc", + chainId: 42161, + methodDetails: undefined, + }, +}; + +const noRequest: MppChallenge = { + id: "c5", + method: "tempo", + intent: "charge", + realm: undefined, + expires: undefined, + request: undefined, +}; + +function defaultSelector(): MppMethodSelector { + return new MppMethodSelector(new Set(), []); +} + +describe("buildMppQuote", () => { + describe("single charge challenge", () => { + it("returns ProtocolQuote with stripe charge converted via Money.fromCents", () => { + const quote = buildMppQuote([stripeCharge], defaultSelector()); + expect(quote.protocol).toBe("mpp"); + expect(quote.scheme).toBe("mpp"); + expect(quote.selectedMethod).toBe("stripe"); + expect(quote.amount.cents).toBe(500n); + expect(quote.amount.currency).toBe("USD"); + expect(quote.payTo).toBe("acct_123"); + expect(quote.allMethods).toBeUndefined(); + }); + + it("returns ProtocolQuote with tempo charge using usdcAtomicToCents", () => { + const quote = buildMppQuote([tempoCharge], defaultSelector()); + expect(quote.protocol).toBe("mpp"); + expect(quote.selectedMethod).toBe("tempo"); + // 1_000_000 atomic / 10_000 = 100 cents = $1.00 + expect(quote.amount.cents).toBe(100n); + expect(quote.amount.currency).toBe("USD"); + expect(quote.payTo).toBe("0xabc"); + }); + + it("returns ProtocolQuote with lightning charge via Money.fromSatoshis", () => { + const quote = buildMppQuote([lightningCharge], defaultSelector()); + expect(quote.protocol).toBe("mpp"); + expect(quote.selectedMethod).toBe("lightning"); + expect(quote.amount.cents).toBe(1000n); + expect(quote.amount.currency).toBe("SATS"); + expect(quote.payTo).toBe("lnbc1..."); + }); + }); + + describe("multi-method challenges", () => { + it("selects cheapest and includes allMethods when multiple methods", () => { + const selector = new MppMethodSelector(new Set(), []); + const quote = buildMppQuote([tempoCharge, stripeCharge], selector); + + expect(quote.allMethods).toBeDefined(); + expect(quote.allMethods).toHaveLength(2); + // Tempo: 100 cents, Stripe: 500 cents -> tempo is cheapest + expect(quote.selectedMethod).toBe("tempo"); + expect(quote.amount.cents).toBe(100n); + }); + + it("allMethods contains both methods with correct amounts", () => { + const quote = buildMppQuote( + [tempoCharge, stripeCharge], + defaultSelector(), + ); + const methods = quote.allMethods!; + const tempoMethod = methods.find((m) => m.method === "tempo"); + const stripeMethod = methods.find((m) => m.method === "stripe"); + + expect(tempoMethod).toBeDefined(); + expect(tempoMethod!.amount.cents).toBe(100n); + expect(stripeMethod).toBeDefined(); + expect(stripeMethod!.amount.cents).toBe(500n); + }); + }); + + describe("error cases", () => { + it("throws MppQuoteError when only session challenges present", () => { + expect(() => buildMppQuote([sessionOnly], defaultSelector())).toThrow( + MppQuoteError, + ); + }); + + it("throws MppQuoteError when empty challenges array", () => { + expect(() => buildMppQuote([], defaultSelector())).toThrow( + MppQuoteError, + ); + }); + + it("throws MppQuoteError for negative amount", () => { + const negative: MppChallenge = { + id: "neg", + method: "stripe", + intent: "charge", + realm: undefined, + expires: undefined, + request: { + amount: "-100", + currency: "USD", + recipient: "acct_123", + chainId: undefined, + methodDetails: undefined, + }, + }; + expect(() => buildMppQuote([negative], defaultSelector())).toThrow( + MppQuoteError, + ); + }); + }); + + describe("no-request challenge", () => { + it("returns Money.zero() with priceUnknown=true", () => { + const quote = buildMppQuote([noRequest], defaultSelector()); + expect(quote.amount.isZero()).toBe(true); + expect(quote.priceUnknown).toBe(true); + expect(quote.selectedMethod).toBe("tempo"); + }); + }); + + describe("mixed charge and session", () => { + it("ignores session challenges and quotes only charge", () => { + const quote = buildMppQuote( + [sessionOnly, stripeCharge], + defaultSelector(), + ); + expect(quote.selectedMethod).toBe("stripe"); + expect(quote.amount.cents).toBe(500n); + expect(quote.allMethods).toBeUndefined(); + }); + }); +}); diff --git a/packages/protocols/tests/x402/x402-parsing.test.ts b/packages/protocols/tests/x402/x402-parsing.test.ts index 76e0204..bbc7e56 100644 --- a/packages/protocols/tests/x402/x402-parsing.test.ts +++ b/packages/protocols/tests/x402/x402-parsing.test.ts @@ -147,6 +147,29 @@ describe("parseWwwAuthenticate", () => { const result = parseWwwAuthenticate(header); expect(result?.accepts[0]?.network).toBe("eip155:8453"); }); + + it("should tolerate spaces around = in params", () => { + const header = 'x402 address = "0xabc", amount = "0.01", chainId = "8453"'; + const result = parseWwwAuthenticate(header); + expect(result).not.toBeNull(); + expect(result?.accepts[0]?.payTo).toBe("0xabc"); + expect(result?.accepts[0]?.amount).toBe("10000"); + }); + + it("should tolerate unquoted param values", () => { + const header = "x402 address=0xabc, amount=0.01"; + const result = parseWwwAuthenticate(header); + expect(result).not.toBeNull(); + expect(result?.accepts[0]?.payTo).toBe("0xabc"); + }); + + it("should handle case-insensitive param names", () => { + const header = 'x402 Address="0xabc", Amount="0.05", ChainId="1"'; + const result = parseWwwAuthenticate(header); + expect(result).not.toBeNull(); + expect(result?.accepts[0]?.payTo).toBe("0xabc"); + expect(result?.accepts[0]?.network).toBe("eip155:1"); + }); }); describe("usdcDisplayToAtomic", () => { diff --git a/packages/sdk/src/bazaar.ts b/packages/sdk/src/bazaar.ts index 148f985..28d5dcf 100644 --- a/packages/sdk/src/bazaar.ts +++ b/packages/sdk/src/bazaar.ts @@ -148,6 +148,7 @@ export async function fetchBazaarDirectory( cache = { data: entries, timestamp: Date.now() }; return entries; } catch { + // Intent: Bazaar API is best-effort — network failure returns empty, SDK proceeds with static directory return []; } } diff --git a/packages/sdk/src/boltzpay.ts b/packages/sdk/src/boltzpay.ts index 5b20722..c6abf04 100644 --- a/packages/sdk/src/boltzpay.ts +++ b/packages/sdk/src/boltzpay.ts @@ -41,11 +41,9 @@ import type { DiscoverOptions, } from "./directory"; import { - classifyProbeError, - DISCOVER_PROBE_TIMEOUT_MS, filterEntries, sortDiscoveredEntries, - withTimeout, + toDiscoverStatus, } from "./directory"; import { BoltzPayError } from "./errors/boltzpay-error"; import { BudgetExceededError } from "./errors/budget-exceeded-error"; @@ -173,6 +171,7 @@ function extractServerMessage( try { text = new TextDecoder().decode(body); } catch { + // Intent: binary or non-UTF-8 body cannot be decoded — return no message return undefined; } if (!text.trim()) return undefined; @@ -188,7 +187,9 @@ function extractServerMessage( if (typeof nested.message === "string") return nested.message; } } - } catch {} + } catch { + // Intent: response body may not be valid JSON — fall through to raw text truncation + } return text.length > SERVER_MESSAGE_MAX_LENGTH ? `${text.slice(0, SERVER_MESSAGE_MAX_LENGTH)}…` @@ -306,9 +307,12 @@ function resolveMaxHistoryRecords(config: ValidatedConfig): number { const LEGACY_DATA_DIR = ".boltzpay"; +const RESOLVED_WALLET_TYPES = ["coinbase", "nwc", "stripe-mpp", "tempo", "visa-mpp"] as const; +type ResolvedWalletType = (typeof RESOLVED_WALLET_TYPES)[number]; + interface ResolvedWallet { readonly name: string; - readonly type: "coinbase" | "nwc"; + readonly type: ResolvedWalletType; readonly cdpManager?: CdpWalletManager; readonly nwcManager?: NwcWalletManager; readonly networks?: readonly string[]; @@ -412,12 +416,21 @@ export class BoltzPay { networks: wc.networks, }; } - const wc = walletConfig; + if (walletConfig.type === "nwc") { + const wc = walletConfig; + return { + name: wc.name, + type: "nwc", + nwcManager: new NwcWalletManager(wc.nwcConnectionString), + networks: wc.networks, + }; + } + // MPP wallet types (stripe-mpp, tempo, visa-mpp) store config only; + // payment execution wired in MppAdapter (Phase 13 Plan 03). return { - name: wc.name, - type: "nwc", - nwcManager: new NwcWalletManager(wc.nwcConnectionString), - networks: wc.networks, + name: walletConfig.name, + type: walletConfig.type, + networks: walletConfig.networks, }; } @@ -609,7 +622,9 @@ export class BoltzPay { "Legacy v0.1 data files detected. v0.2 uses a new storage format — starting fresh.", ); } - } catch {} + } catch { + // Intent: legacy v0.1 detection is best-effort — missing dir is normal on fresh installs + } } private buildRetryOptions(phase: string): RetryOptions { @@ -707,6 +722,7 @@ export class BoltzPay { try { selectedQuote = this.selectPaymentChain(primary.quote, options); } catch { + // Intent: no compatible chain available — report as detection_failed with partial quote return { wouldPay: false, reason: "detection_failed", @@ -901,6 +917,7 @@ export class BoltzPay { try { return parseNetworkIdentifier(network); } catch { + // Intent: non-standard network identifier — treat as unknown rather than crashing return undefined; } } @@ -1403,6 +1420,7 @@ export class BoltzPay { return result; } catch { + // Intent: wallet balance query failure is non-fatal — return empty balances return {}; } } @@ -1530,11 +1548,14 @@ export class BoltzPay { async discover( options?: DiscoverOptions, ): Promise { + await this.initPromise; const live = options?.enableLiveDiscovery ?? true; const allEntries = await getMergedDirectory({ live }); const entries = filterEntries(allEntries, options?.category); const results = await Promise.all( - entries.map((entry) => this.probeDirectoryEntry(entry, options?.signal)), + entries.map((entry) => + this.probeDirectoryEntry(entry, options?.signal), + ), ); return sortDiscoveredEntries(results); } @@ -1543,24 +1564,13 @@ export class BoltzPay { entry: ApiDirectoryEntry, signal?: AbortSignal, ): Promise { - try { - const result = await withTimeout( - this.quote(entry.url), - DISCOVER_PROBE_TIMEOUT_MS, - signal, - ); - return { - ...entry, - live: { - status: "live", - livePrice: result.amount.toDisplayString(), - protocol: result.protocol, - network: result.network, - }, - }; - } catch (err) { - return { ...entry, live: classifyProbeError(err) }; - } + const result = await diagnoseEndpoint({ + url: entry.url, + router: this.router, + detectTimeoutMs: this.config.timeouts?.detect, + signal, + }); + return { ...entry, live: toDiscoverStatus(result) }; } close(): void { diff --git a/packages/sdk/src/budget/budget-manager.ts b/packages/sdk/src/budget/budget-manager.ts index 5b9adb6..18dcebf 100644 --- a/packages/sdk/src/budget/budget-manager.ts +++ b/packages/sdk/src/budget/budget-manager.ts @@ -87,7 +87,9 @@ export class BudgetManager { if (data.lastMonthlyReset === month) { this.monthlySpent = Money.fromCents(BigInt(data.monthlySpent)); } - } catch {} + } catch { + // Intent: corrupted budget state starts fresh — no data loss, only resets counters + } } convertToUsd(amount: Money): Money { diff --git a/packages/sdk/src/config/schema.ts b/packages/sdk/src/config/schema.ts index 91cb416..4d882a9 100644 --- a/packages/sdk/src/config/schema.ts +++ b/packages/sdk/src/config/schema.ts @@ -85,9 +85,33 @@ const NwcWalletSchema = z.object({ networks: z.array(z.string().min(1)).optional(), }); +const StripeMppWalletSchema = z.object({ + type: z.literal("stripe-mpp"), + name: z.string().min(1), + stripeSecretKey: z.string().min(1), + networks: z.array(z.string().min(1)).optional(), +}); + +const TempoWalletSchema = z.object({ + type: z.literal("tempo"), + name: z.string().min(1), + tempoPrivateKey: z.string().min(1), + networks: z.array(z.string().min(1)).optional(), +}); + +const VisaMppWalletSchema = z.object({ + type: z.literal("visa-mpp"), + name: z.string().min(1), + visaJwe: z.string().min(1), + networks: z.array(z.string().min(1)).optional(), +}); + export const WalletSchema = z.discriminatedUnion("type", [ CoinbaseWalletSchema, NwcWalletSchema, + StripeMppWalletSchema, + TempoWalletSchema, + VisaMppWalletSchema, ]); export const BoltzPayConfigSchema = z.object({ @@ -116,6 +140,7 @@ export const BoltzPayConfigSchema = z.object({ retry: RetrySchema.optional(), rateLimit: RateLimitSchema.optional(), wallets: z.array(WalletSchema).optional(), + mppPreferredMethods: z.array(z.string().min(1)).optional(), }); function formatZodIssues(issues: ReadonlyArray): string { diff --git a/packages/sdk/src/diagnostics/diagnose.ts b/packages/sdk/src/diagnostics/diagnose.ts index c3c7b0b..5246c92 100644 --- a/packages/sdk/src/diagnostics/diagnose.ts +++ b/packages/sdk/src/diagnostics/diagnose.ts @@ -1,8 +1,14 @@ import { resolve as dnsResolve } from "node:dns/promises"; -import type { AcceptOption, EndpointInputHints } from "@boltzpay/core"; +import type { AcceptOption, EndpointInputHints, MppMethodQuote } from "@boltzpay/core"; import { Money } from "@boltzpay/core"; -import type { NegotiatedPayment } from "@boltzpay/protocols"; -import { negotiatePayment, type ProtocolRouter } from "@boltzpay/protocols"; +import type { MppChallenge, NegotiatedPayment } from "@boltzpay/protocols"; +import { + hasMppScheme, + negotiatePayment, + parseMppChallenges, + type ProtocolRouter, + usdcAtomicToCents, +} from "@boltzpay/protocols"; export type EndpointHealth = "healthy" | "degraded" | "dead"; @@ -15,11 +21,12 @@ export type EndpointClassification = export type DeathReason = | "dns_failure" | "http_404" + | "http_405" | "http_5xx" | "timeout" | "tls_error"; -export type FormatVersion = "V1 body" | "V2 header" | "www-authenticate"; +export type FormatVersion = "V1 body" | "V2 header" | "www-authenticate" | "mpp"; export interface ChainInfo { readonly namespace: string; @@ -34,6 +41,17 @@ export interface DiagnoseTiming { readonly quoteMs: number; } +export interface MppMethodDetail { + readonly method: string; + readonly intent: string; + readonly id: string | undefined; + readonly expires: string | undefined; + readonly rawAmount: string | undefined; + readonly currency: string | undefined; + readonly recipient: string | undefined; + readonly chainId: number | undefined; +} + export interface DiagnoseResult { readonly url: string; readonly classification: EndpointClassification; @@ -53,6 +71,7 @@ export interface DiagnoseResult { readonly rawAccepts?: readonly AcceptOption[]; readonly inputHints?: EndpointInputHints; readonly timing?: DiagnoseTiming; + readonly mppMethods?: readonly MppMethodDetail[]; } const TRUNCATE_THRESHOLD = 13; @@ -65,15 +84,22 @@ export function truncateAddress(addr: string): string { } const SLOW_THRESHOLD_MS = 1000; +const STELLAR_SLOW_THRESHOLD_MS = 5000; +const SUSPICIOUS_PRICE_THRESHOLD = Money.fromDollars("100.00"); export function classifyHealth( latencyMs: number, scheme: string | undefined, network: string | undefined, + price?: Money, ): EndpointHealth { if (scheme && scheme !== "exact") return "degraded"; - if (network?.startsWith("stellar")) return "degraded"; - if (latencyMs >= SLOW_THRESHOLD_MS) return "degraded"; + if (price && price.currency === "USD" && price.greaterThan(SUSPICIOUS_PRICE_THRESHOLD)) { + return "degraded"; + } + const isStellar = network?.startsWith("stellar") ?? false; + const threshold = isStellar ? STELLAR_SLOW_THRESHOLD_MS : SLOW_THRESHOLD_MS; + if (latencyMs >= threshold) return "degraded"; return "healthy"; } @@ -105,6 +131,7 @@ export interface DiagnoseInput { readonly url: string; readonly router: ProtocolRouter; readonly detectTimeoutMs?: number; + readonly signal?: AbortSignal; } const DEFAULT_DETECT_TIMEOUT_MS = 10_000; @@ -113,15 +140,21 @@ const DNS_BUDGET_FRACTION = 0.15; const DNS_MIN_MS = 500; const DNS_MAX_MS = 3_000; +const MAX_REDIRECTS = 5; + const POST_BUDGET_FRACTION = 0.4; const POST_MIN_MS = 500; const TLS_ERROR_RE = /tls|ssl|certificate/i; -function hasPaymentHeaders(response: Response): boolean { - if (response.headers.has("x-payment")) return true; +function hasValidPaymentHeaders(response: Response): boolean { + const xPayment = response.headers.get("x-payment"); + if (xPayment && isDecodableBase64Json(xPayment)) return true; + const wwwAuth = response.headers.get("www-authenticate"); if (wwwAuth && /X402|L402/i.test(wwwAuth)) return true; + if (wwwAuth && hasMppScheme(wwwAuth)) return true; + let hasX402Header = false; response.headers.forEach((_value, key) => { if (key.toLowerCase().startsWith("x402-")) hasX402Header = true; @@ -129,6 +162,17 @@ function hasPaymentHeaders(response: Response): boolean { return hasX402Header; } +function isDecodableBase64Json(value: string): boolean { + try { + const decoded = Buffer.from(value, "base64").toString("utf-8"); + JSON.parse(decoded); + return true; + } catch { + // Intent: malformed x-payment header should not trigger "paid" classification + return false; + } +} + function classifyFetchError(error: unknown): DeathReason { if (error instanceof Error && TLS_ERROR_RE.test(error.message)) { return "tls_error"; @@ -150,6 +194,7 @@ async function resolveDns( ]); return true; } catch { + // Intent: DNS failure means endpoint is unreachable — caller handles the false return return false; } finally { if (timer) clearTimeout(timer); @@ -160,26 +205,60 @@ async function timedFetch( url: string, init: RequestInit, timeoutMs: number, + externalSignal?: AbortSignal, ): Promise { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); + const onAbort = () => controller.abort(); + externalSignal?.addEventListener("abort", onAbort, { once: true }); try { return await globalThis.fetch(url, { ...init, signal: controller.signal }); } finally { clearTimeout(timer); + externalSignal?.removeEventListener("abort", onAbort); } } +async function fetchFollowingRedirects( + url: string, + timeoutMs: number, + externalSignal?: AbortSignal, +): Promise { + const visited = new Set(); + let currentUrl = url; + for (let i = 0; i <= MAX_REDIRECTS; i++) { + if (visited.has(currentUrl)) { + throw new Error(`Redirect cycle: ${currentUrl}`); + } + visited.add(currentUrl); + const response = await timedFetch( + currentUrl, + { method: "GET", redirect: "manual" }, + timeoutMs, + externalSignal, + ); + if (response.status < 300 || response.status >= 400) return response; + const location = response.headers.get("location"); + if (!location) return response; + currentUrl = new URL(location, currentUrl).href; + } + throw new Error("Too many redirects"); +} + export async function diagnoseEndpoint( input: DiagnoseInput, ): Promise { - const { url, router, detectTimeoutMs } = input; + const { url, router, detectTimeoutMs, signal } = input; const totalBudget = detectTimeoutMs ?? DEFAULT_DETECT_TIMEOUT_MS; const totalStart = Date.now(); const remainingBudget = () => Math.max(0, totalBudget - (Date.now() - totalStart)); + if (signal?.aborted) { + return buildDeadResult(url, 0, "timeout"); + } + try { const dnsBudget = Math.min( DNS_MAX_MS, @@ -195,10 +274,10 @@ export async function diagnoseEndpoint( let response: Response; try { - response = await timedFetch( + response = await fetchFollowingRedirects( url, - { method: "GET", redirect: "follow" }, remainingBudget(), + signal, ); } catch (error) { return buildDeadResult( @@ -221,8 +300,13 @@ export async function diagnoseEndpoint( ); } - if (status === 404 || status === 410) { - return buildDeadResult(url, Date.now() - totalStart, "http_404", status); + if (status === 404 || status === 405 || status === 410) { + const postProbe = await tryPostProbe( + url, totalStart, detectStart, remainingBudget, signal, router, + ); + if (postProbe.kind === "paid") return postProbe.result; + const deathReason: DeathReason = status === 405 ? "http_405" : "http_404"; + return buildDeadResult(url, Date.now() - totalStart, deathReason, status); } if (status >= 500 && status < 600) { @@ -230,7 +314,7 @@ export async function diagnoseEndpoint( } if (status >= 200 && status < 300) { - if (hasPaymentHeaders(response)) { + if (hasValidPaymentHeaders(response)) { return buildPaidResult( url, totalStart, @@ -241,45 +325,24 @@ export async function diagnoseEndpoint( ); } - const postBudget = Math.max( - POST_MIN_MS, - remainingBudget() * POST_BUDGET_FRACTION, + const postProbe = await tryPostProbe( + url, totalStart, detectStart, remainingBudget, signal, router, ); - try { - const postResponse = await timedFetch( - url, - { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: "{}", - redirect: "follow", - }, - postBudget, - ); - - if (postResponse.status === 402) { - return buildPaidResult( - url, - totalStart, - detectStart, - postResponse, - true, - router, - ); - } - - return buildNonPaidResult( - url, - Date.now() - totalStart, - "free_confirmed", - ); - } catch { + if (postProbe.kind === "paid") return postProbe.result; + if (postProbe.kind === "failed") { return buildNonPaidResult(url, Date.now() - totalStart, "ambiguous"); } + + return buildNonPaidResult( + url, + Date.now() - totalStart, + "free_confirmed", + ); } return buildNonPaidResult(url, Date.now() - totalStart, "ambiguous"); } catch { + // Intent: global budget exhausted or unexpected error — classify as timeout return buildDeadResult(url, Date.now() - totalStart, "timeout"); } } @@ -308,6 +371,53 @@ function buildDeadResult( }; } +type PostProbeOutcome = + | { readonly kind: "paid"; readonly result: DiagnoseResult } + | { readonly kind: "not_paid" } + | { readonly kind: "failed" }; + +async function tryPostProbe( + url: string, + totalStart: number, + detectStart: number, + remainingBudget: () => number, + signal: AbortSignal | undefined, + router: ProtocolRouter, +): Promise { + const postBudget = Math.max( + POST_MIN_MS, + remainingBudget() * POST_BUDGET_FRACTION, + ); + try { + const postResponse = await timedFetch( + url, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "{}", + redirect: "follow", + }, + postBudget, + signal, + ); + if (postResponse.status === 402) { + const result = await buildPaidResult( + url, + totalStart, + detectStart, + postResponse, + true, + router, + ); + return { kind: "paid", result }; + } + return { kind: "not_paid" }; + } catch { + // Intent: POST probe network failure — cannot determine paid/free + return { kind: "failed" }; + } +} + function buildNonPaidResult( url: string, latencyMs: number, @@ -343,7 +453,9 @@ async function buildPaidResult( if (negotiation) { formatVersion = toFormatVersion(negotiation); } - } catch {} + } catch { + // Intent: format negotiation is best-effort; diagnosis proceeds without version info + } const detectMs = Date.now() - detectStart; @@ -354,11 +466,23 @@ async function buildPaidResult( const primaryProbe = probeResults[0]; if (!primaryProbe) { + const mpp = tryDetectMpp(response.clone()); + if (mpp) { + const latencyMs = Date.now() - totalStart; + return buildMppDiagnoseResult( + url, + latencyMs, + mpp, + postOnly, + { detectMs, quoteMs }, + ); + } const latencyMs = Date.now() - totalStart; + const hasValidFormat = formatVersion !== undefined; return { url, - classification: "paid", - isPaid: true, + classification: hasValidFormat ? "paid" : "ambiguous", + isPaid: hasValidFormat, protocol: undefined, formatVersion, scheme: undefined, @@ -385,12 +509,114 @@ async function buildPaidResult( network: quote.network, price: quote.amount, facilitator: quote.payTo ? truncateAddress(quote.payTo) : undefined, - health: classifyHealth(latencyMs, quote.scheme, quote.network), + health: classifyHealth(latencyMs, quote.scheme, quote.network, quote.amount), latencyMs, postOnly, chains: buildChains(quote.allAccepts), rawAccepts: quote.allAccepts, ...(quote.inputHints ? { inputHints: quote.inputHints } : {}), + ...(quote.allMethods ? { mppMethods: quoteMppMethodsToDetails(quote.allMethods) } : {}), timing: { detectMs, quoteMs }, }; } + +interface MppDetection { + readonly primary: MppChallenge; + readonly all: readonly MppChallenge[]; +} + +function tryDetectMpp(response: Response): MppDetection | undefined { + const wwwAuth = response.headers.get("www-authenticate"); + if (!wwwAuth) return undefined; + const { challenges } = parseMppChallenges(wwwAuth); + const primary = challenges[0]; + if (!primary) return undefined; + return { primary, all: challenges }; +} + +function buildMppDiagnoseResult( + url: string, + latencyMs: number, + mpp: MppDetection, + postOnly: boolean, + timing: DiagnoseTiming, +): DiagnoseResult { + const price = tryExtractMppPrice(mpp.primary); + const network = deriveMppNetwork(mpp.primary); + const facilitator = mpp.primary.request?.recipient + ? truncateAddress(mpp.primary.request.recipient) + : undefined; + return { + url, + classification: "paid", + isPaid: true, + protocol: "mpp", + formatVersion: "mpp", + scheme: "exact", + network, + price, + facilitator, + health: classifyHealth(latencyMs, "exact", network, price), + latencyMs, + postOnly, + mppMethods: toMppMethodDetails(mpp.all), + timing, + }; +} + +function toMppMethodDetails( + challenges: readonly MppChallenge[], +): readonly MppMethodDetail[] { + return challenges.map((c) => ({ + method: c.method, + intent: c.intent, + id: c.id, + expires: c.expires, + rawAmount: c.request?.amount, + currency: c.request?.currency, + recipient: c.request?.recipient, + chainId: c.request?.chainId, + })); +} + +function quoteMppMethodsToDetails( + methods: readonly MppMethodQuote[], +): readonly MppMethodDetail[] { + return methods.map((m) => ({ + method: m.method, + intent: m.intent, + id: undefined, + expires: undefined, + rawAmount: m.amount.cents.toString(), + currency: m.currency, + recipient: m.recipient, + chainId: m.network ? Number(m.network) || undefined : undefined, + })); +} + +function tryExtractMppPrice(challenge: MppChallenge): Money | undefined { + if (!challenge.request) return undefined; + try { + const rawAmount = BigInt(challenge.request.amount); + if (rawAmount < 0n) return undefined; + // Stripe amounts are in smallest currency unit (cents for USD) + if (challenge.method === "stripe") { + return Money.fromCents(rawAmount); + } + // Crypto methods with chainId: assume 6-decimal stablecoin (USDC/USDT) + if (challenge.request.chainId !== undefined) { + return Money.fromCents(usdcAtomicToCents(rawAmount)); + } + return undefined; + } catch { + // Intent: price extraction is best-effort — diagnosis proceeds without price + return undefined; + } +} + +function deriveMppNetwork(challenge: MppChallenge): string | undefined { + if (challenge.request?.chainId !== undefined) { + return `eip155:${challenge.request.chainId}`; + } + return undefined; +} diff --git a/packages/sdk/src/diagnostics/dry-run.ts b/packages/sdk/src/diagnostics/dry-run.ts index b432934..c67c7d7 100644 --- a/packages/sdk/src/diagnostics/dry-run.ts +++ b/packages/sdk/src/diagnostics/dry-run.ts @@ -28,7 +28,7 @@ export interface DryRunResult { }; readonly wallet?: { readonly name: string; - readonly type: "coinbase" | "nwc"; + readonly type: string; }; readonly endpoint?: { readonly health: EndpointHealth; diff --git a/packages/sdk/src/directory.ts b/packages/sdk/src/directory.ts index 3a7855f..ed7adc4 100644 --- a/packages/sdk/src/directory.ts +++ b/packages/sdk/src/directory.ts @@ -1,5 +1,4 @@ -import { NetworkError } from "./errors/network-error"; -import { ProtocolError } from "./errors/protocol-error"; +import type { DiagnoseResult } from "./diagnostics/diagnose"; const REMOTE_DIRECTORY_URL = "https://raw.githubusercontent.com/leventilo/boltzpay/main/directory.json"; @@ -51,6 +50,7 @@ export async function fetchRemoteDirectory(): Promise< directoryCache = { data, timestamp: Date.now() }; return data; } catch { + // Intent: remote directory fetch is best-effort — fall back to embedded static directory return API_DIRECTORY; } } @@ -508,7 +508,6 @@ export function getDirectoryCategories(): string[] { return [...new Set(API_DIRECTORY.map((e) => e.category))]; } -export const DISCOVER_PROBE_TIMEOUT_MS = 5_000; /** Filter the static API directory by category. Returns all entries if no category given. */ export function filterDirectory( @@ -529,26 +528,24 @@ export function filterEntries( return entries.filter((e) => e.category === lower); } -export function classifyProbeError(err: unknown): DiscoverEntryStatus { - if ( - err instanceof ProtocolError && - err.code === "protocol_detection_failed" - ) { - return { status: "free" }; - } - if (err instanceof NetworkError) { - return { status: "offline", reason: err.message }; - } - if (err instanceof Error && err.name === "TimeoutError") { - return { status: "offline", reason: "Timeout" }; - } - if (err instanceof Error && err.name === "AbortError") { - return { status: "offline", reason: "Aborted" }; +export function toDiscoverStatus( + result: DiagnoseResult, +): DiscoverEntryStatus { + switch (result.classification) { + case "paid": + return { + status: "live", + livePrice: result.price?.toDisplayString() ?? "unknown", + protocol: result.protocol ?? "unknown", + network: result.network, + }; + case "free_confirmed": + return { status: "free" }; + case "dead": + return { status: "offline", reason: result.deathReason ?? "unknown" }; + case "ambiguous": + return { status: "error", reason: "Ambiguous — 402 without valid payment options" }; } - return { - status: "error", - reason: err instanceof Error ? err.message : String(err), - }; } const STATUS_ORDER: Record = { diff --git a/packages/sdk/src/history/payment-history.ts b/packages/sdk/src/history/payment-history.ts index 6d213bd..03c3b8b 100644 --- a/packages/sdk/src/history/payment-history.ts +++ b/packages/sdk/src/history/payment-history.ts @@ -47,6 +47,7 @@ function deserializeRecord(raw: string): PaymentRecord | undefined { durationMs: data.durationMs, }; } catch { + // Intent: corrupted history line is silently skipped — partial history is acceptable return undefined; } } diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 86a12ad..8cca638 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -21,14 +21,17 @@ export type { ValidatedConfig, WalletConfig, } from "./config/types"; +export { diagnoseEndpoint } from "./diagnostics/diagnose"; export type { ChainInfo, DeathReason, + DiagnoseInput, DiagnoseResult, DiagnoseTiming, EndpointClassification, EndpointHealth, FormatVersion, + MppMethodDetail, } from "./diagnostics/diagnose"; export type { DryRunFailureReason, @@ -95,4 +98,4 @@ export type { WalletStatus, } from "./wallet-status"; -export const VERSION = "0.1.1"; +export const VERSION = "0.2.1"; diff --git a/packages/sdk/src/persistence/file-adapter.ts b/packages/sdk/src/persistence/file-adapter.ts index 91e83f2..8550004 100644 --- a/packages/sdk/src/persistence/file-adapter.ts +++ b/packages/sdk/src/persistence/file-adapter.ts @@ -40,7 +40,9 @@ export class FileAdapter implements StorageAdapter { } catch (err: unknown) { try { await unlink(tmpPath); - } catch {} + } catch { + // Intent: tmp file cleanup is best-effort — OS will reclaim if unlink fails + } throw err; } } diff --git a/packages/sdk/src/retry/retry-engine.ts b/packages/sdk/src/retry/retry-engine.ts index 27d6780..a8d11b0 100644 --- a/packages/sdk/src/retry/retry-engine.ts +++ b/packages/sdk/src/retry/retry-engine.ts @@ -30,6 +30,9 @@ const TRANSIENT_ERROR_PATTERNS = [ "enotfound", "fetch failed", "network error", + "bad gateway", + "service unavailable", + "gateway timeout", ]; export async function withRetry( diff --git a/packages/sdk/tests/config/config-validation.test.ts b/packages/sdk/tests/config/config-validation.test.ts index de4318a..866c0ab 100644 --- a/packages/sdk/tests/config/config-validation.test.ts +++ b/packages/sdk/tests/config/config-validation.test.ts @@ -396,6 +396,154 @@ describe("Config Validation", () => { }); }); + describe("Phase 13: MPP wallet schemas", () => { + it("validates stripe-mpp wallet config", () => { + const result = validateConfig({ + wallets: [ + { + type: "stripe-mpp", + name: "stripe-prod", + stripeSecretKey: "sk_live_abc123", + }, + ], + }); + expect(result.wallets).toHaveLength(1); + expect(result.wallets![0]).toEqual( + expect.objectContaining({ type: "stripe-mpp", name: "stripe-prod" }), + ); + }); + + it("validates tempo wallet config", () => { + const result = validateConfig({ + wallets: [ + { + type: "tempo", + name: "tempo-main", + tempoPrivateKey: "0xdeadbeef", + }, + ], + }); + expect(result.wallets).toHaveLength(1); + expect(result.wallets![0]).toEqual( + expect.objectContaining({ type: "tempo", name: "tempo-main" }), + ); + }); + + it("validates visa-mpp wallet config", () => { + const result = validateConfig({ + wallets: [ + { + type: "visa-mpp", + name: "visa-card", + visaJwe: "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ", + }, + ], + }); + expect(result.wallets).toHaveLength(1); + expect(result.wallets![0]).toEqual( + expect.objectContaining({ type: "visa-mpp", name: "visa-card" }), + ); + }); + + it("existing coinbase wallet still parses after extension", () => { + const result = validateConfig({ + wallets: [ + { + type: "coinbase", + name: "prod", + coinbaseApiKeyId: "key-id", + coinbaseApiKeySecret: "key-secret", + coinbaseWalletSecret: "wallet-secret", + }, + ], + }); + expect(result.wallets![0]).toEqual( + expect.objectContaining({ type: "coinbase" }), + ); + }); + + it("existing nwc wallet still parses after extension", () => { + const result = validateConfig({ + wallets: [ + { + type: "nwc", + name: "ln", + nwcConnectionString: "nostr+walletconnect://relay.example.com", + }, + ], + }); + expect(result.wallets![0]).toEqual( + expect.objectContaining({ type: "nwc" }), + ); + }); + + it("rejects stripe-mpp wallet without stripeSecretKey", () => { + expect(() => + validateConfig({ + wallets: [{ type: "stripe-mpp", name: "bad" }], + }), + ).toThrow(ConfigurationError); + }); + + it("rejects tempo wallet without tempoPrivateKey", () => { + expect(() => + validateConfig({ + wallets: [{ type: "tempo", name: "bad" }], + }), + ).toThrow(ConfigurationError); + }); + + it("rejects visa-mpp wallet without visaJwe", () => { + expect(() => + validateConfig({ + wallets: [{ type: "visa-mpp", name: "bad" }], + }), + ).toThrow(ConfigurationError); + }); + + it("validates stripe-mpp wallet with networks array", () => { + const result = validateConfig({ + wallets: [ + { + type: "stripe-mpp", + name: "stripe", + stripeSecretKey: "sk_live_abc", + networks: ["base"], + }, + ], + }); + expect(result.wallets![0]).toEqual( + expect.objectContaining({ networks: ["base"] }), + ); + }); + }); + + describe("Phase 13: mppPreferredMethods config", () => { + it('accepts mppPreferredMethods: ["tempo", "stripe"]', () => { + const result = validateConfig({ + mppPreferredMethods: ["tempo", "stripe"], + }); + expect(result.mppPreferredMethods).toEqual(["tempo", "stripe"]); + }); + + it("leaves mppPreferredMethods undefined when not provided", () => { + const result = validateConfig({}); + expect(result.mppPreferredMethods).toBeUndefined(); + }); + + it("rejects mppPreferredMethods with empty string", () => { + expect(() => + validateConfig({ mppPreferredMethods: [""] }), + ).toThrow(ConfigurationError); + }); + + it("existing config without MPP fields still parses", () => { + const result = validateConfig(validBase); + expect(result.mppPreferredMethods).toBeUndefined(); + expect(result.coinbaseApiKeyId).toBe("test-key-id"); + }); + }); + describe("Phase 10: StorageSchema", () => { it('accepts storage: "file" shortcut', () => { const result = validateConfig({ storage: "file" }); diff --git a/packages/sdk/tests/diagnostics/diagnose.test.ts b/packages/sdk/tests/diagnostics/diagnose.test.ts index 7498c4e..4bed1e5 100644 --- a/packages/sdk/tests/diagnostics/diagnose.test.ts +++ b/packages/sdk/tests/diagnostics/diagnose.test.ts @@ -17,6 +17,8 @@ const mockProbe = vi.fn(); const mockProbeAll = vi.fn(); const mockProbeFromResponse = vi.fn(); const mockExecute = vi.fn(); +const mockHasMppScheme = vi.fn().mockReturnValue(false); +const mockParseMppChallenges = vi.fn().mockReturnValue({ challenges: [] }); vi.mock("@boltzpay/protocols", () => { class MockCdpWalletManager { @@ -78,6 +80,15 @@ vi.mock("@boltzpay/protocols", () => { X402PaymentError: MockX402PaymentError, AggregatePaymentError: MockAggregatePaymentError, negotiatePayment: vi.fn(), + hasMppScheme: (value: string) => mockHasMppScheme(value), + parseMppChallenges: (value: string) => mockParseMppChallenges(value), + usdcAtomicToCents: (atomic: bigint) => { + if (atomic < 0n) throw new Error("Atomic units cannot be negative"); + if (atomic === 0n) return 0n; + const ATOMIC_PER_CENT = 10_000n; + const cents = (atomic + ATOMIC_PER_CENT - 1n) / ATOMIC_PER_CENT; + return cents < 1n ? 1n : cents; + }, }; }); @@ -86,6 +97,7 @@ import { negotiatePayment } from "@boltzpay/protocols"; // Import AFTER mocks import { BoltzPay } from "../../src/boltzpay"; import type { DiagnoseResult } from "../../src/diagnostics/diagnose"; +import { classifyHealth } from "../../src/diagnostics/diagnose"; const mockedNegotiatePayment = vi.mocked(negotiatePayment); const mockedDnsResolve = vi.mocked(dns.resolve); @@ -128,6 +140,8 @@ describe("diagnose — endpoint diagnostic report", () => { mockProbeFromResponse.mockReset(); mockProbeFromResponse.mockResolvedValue([]); mockExecute.mockReset(); + mockHasMppScheme.mockReset().mockReturnValue(false); + mockParseMppChallenges.mockReset().mockReturnValue({ challenges: [] }); mockedNegotiatePayment.mockReset(); mockedDnsResolve.mockReset(); mockedDnsResolve.mockResolvedValue(["127.0.0.1"] as Awaited< @@ -263,7 +277,7 @@ describe("diagnose — endpoint diagnostic report", () => { expect(result.scheme).toBe("upto"); }); - it("endpoint on stellar network -> health = 'degraded'", async () => { + it("endpoint on stellar network with low latency -> health = 'healthy'", async () => { const quote = makeQuote({ network: "stellar:pubnet", scheme: "exact" }); fetchSpy.mockResolvedValue(make402Response()); mockProbeFromResponse.mockResolvedValue([ @@ -277,7 +291,8 @@ describe("diagnose — endpoint diagnostic report", () => { }); const result = await agent.diagnose("https://api.example.com/data"); - expect(result.health).toBe("degraded"); + expect(result.health).toBe("healthy"); + expect(result.network).toBe("stellar:pubnet"); }); it("endpoint that times out -> health = 'dead'", async () => { @@ -538,10 +553,11 @@ describe("diagnose — endpoint diagnostic report", () => { it("GET returns 200 with x-payment header -> classification = 'paid'", async () => { const quote = makeQuote(); + const validXPayment = Buffer.from(JSON.stringify({ scheme: "exact" })).toString("base64"); fetchSpy.mockResolvedValue( new Response("ok", { status: 200, - headers: { "x-payment": "some-value" }, + headers: { "x-payment": validXPayment }, }), ); mockProbeFromResponse.mockResolvedValue([ @@ -559,6 +575,33 @@ describe("diagnose — endpoint diagnostic report", () => { expect(result.isPaid).toBe(true); }); + it("GET returns 200 with invalid x-payment header -> classification = 'free_confirmed'", async () => { + fetchSpy + .mockResolvedValueOnce( + new Response("ok", { + status: 200, + headers: { "x-payment": "not-valid-base64-json" }, + }), + ) + .mockResolvedValueOnce(new Response("ok", { status: 200 })); + + const result = await agent.diagnose("https://api.example.com/data"); + expect(result.classification).toBe("free_confirmed"); + expect(result.isPaid).toBe(false); + }); + + it("GET returns 402 but no adapter can parse -> classification = 'ambiguous'", async () => { + fetchSpy.mockResolvedValue( + new Response("payment required", { status: 402 }), + ); + mockProbeFromResponse.mockResolvedValue([]); + mockedNegotiatePayment.mockResolvedValue(undefined); + + const result = await agent.diagnose("https://api.example.com/data"); + expect(result.classification).toBe("ambiguous"); + expect(result.isPaid).toBe(false); + }); + it("GET returns 200 with www-authenticate containing X402 -> classification = 'paid'", async () => { const quote = makeQuote(); fetchSpy.mockResolvedValue( @@ -599,5 +642,317 @@ describe("diagnose — endpoint diagnostic report", () => { expect(result.classification).toBe("dead"); expect(result.deathReason).toBe("tls_error"); }); + + it("GET 404, POST 402 -> classification = 'paid', postOnly = true", async () => { + const quote = makeQuote(); + fetchSpy + .mockResolvedValueOnce(new Response("", { status: 404 })) + .mockResolvedValueOnce(make402Response()); + mockedNegotiatePayment.mockResolvedValue({ transport: "body" }); + mockProbeFromResponse.mockResolvedValue([ + { adapter: { name: "x402" }, quote }, + ]); + + const result = await agent.diagnose("https://api.example.com/data"); + expect(result.classification).toBe("paid"); + expect(result.postOnly).toBe(true); + }); + + it("GET 405, POST 402 -> classification = 'paid', postOnly = true", async () => { + const quote = makeQuote(); + fetchSpy + .mockResolvedValueOnce(new Response("", { status: 405 })) + .mockResolvedValueOnce(make402Response()); + mockedNegotiatePayment.mockResolvedValue({ transport: "body" }); + mockProbeFromResponse.mockResolvedValue([ + { adapter: { name: "x402" }, quote }, + ]); + + const result = await agent.diagnose("https://api.example.com/data"); + expect(result.classification).toBe("paid"); + expect(result.postOnly).toBe(true); + }); + + it("GET 405, POST non-402 -> classification = 'dead', deathReason = 'http_405'", async () => { + fetchSpy + .mockResolvedValueOnce(new Response("", { status: 405 })) + .mockResolvedValueOnce(new Response("", { status: 401 })); + + const result = await agent.diagnose("https://api.example.com/data"); + expect(result.classification).toBe("dead"); + expect(result.deathReason).toBe("http_405"); + expect(result.httpStatus).toBe(405); + }); + }); +}); + +describe("diagnose — MPP protocol detection", () => { + let agent: BoltzPay; + let fetchSpy: ReturnType; + + beforeEach(() => { + agent = new BoltzPay(validConfig); + fetchSpy = vi.fn(); + vi.stubGlobal("fetch", fetchSpy); + mockProbe.mockReset(); + mockProbeAll.mockReset(); + mockProbeFromResponse.mockReset(); + mockProbeFromResponse.mockResolvedValue([]); + mockExecute.mockReset(); + mockHasMppScheme.mockReset().mockReturnValue(false); + mockParseMppChallenges.mockReset().mockReturnValue({ challenges: [] }); + mockedNegotiatePayment.mockReset(); + mockedDnsResolve.mockReset(); + mockedDnsResolve.mockResolvedValue(["127.0.0.1"] as Awaited< + ReturnType + >); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + function make402MppResponse(): Response { + return make402Response({ + "www-authenticate": 'Payment method="tempo", intent="charge"', + }); + } + + function makeMppChallenge(overrides?: Record) { + return { + id: "test-id", + method: "tempo", + intent: "charge", + realm: undefined, + expires: undefined, + request: { + amount: "10000", + currency: "0x20C0D54F37EF0E3B2A5E3a7C9Ab0bFe15f2F1b80", + recipient: "0x10409f8a084D05AbC4E12A8dD8d4CeDF41F06Ce2", + chainId: 4217, + methodDetails: { chainId: 4217 }, + }, + ...overrides, + }; + } + + it("402 with MPP Payment header -> classification = 'paid', protocol = 'mpp'", async () => { + fetchSpy.mockResolvedValue( + make402Response({ + "www-authenticate": + 'Payment id="test-id", method="tempo", intent="charge"', + }), + ); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [makeMppChallenge()], + }); + + const result = await agent.diagnose("https://x402.browserbase.com/test"); + expect(result.classification).toBe("paid"); + expect(result.isPaid).toBe(true); + expect(result.protocol).toBe("mpp"); + expect(result.formatVersion).toBe("mpp"); + expect(result.scheme).toBe("exact"); + }); + + it("MPP tempo endpoint exposes network as eip155:chainId", async () => { + fetchSpy.mockResolvedValue(make402MppResponse()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [makeMppChallenge()], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.network).toBe("eip155:4217"); + }); + + it("MPP tempo endpoint extracts price from stablecoin atomic amount", async () => { + fetchSpy.mockResolvedValue(make402MppResponse()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [makeMppChallenge()], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.price).toBeDefined(); + expect(result.price?.equals(Money.fromCents(1n))).toBe(true); + }); + + it("MPP stripe endpoint treats amount as cents", async () => { + fetchSpy.mockResolvedValue(make402MppResponse()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [ + makeMppChallenge({ + method: "stripe", + request: { + amount: "1000", + currency: "usd", + recipient: "acct_test", + chainId: undefined, + methodDetails: undefined, + }, + }), + ], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.price).toBeDefined(); + expect(result.price?.equals(Money.fromCents(1000n))).toBe(true); + expect(result.network).toBeUndefined(); + }); + + it("MPP multi-method populates mppMethods array", async () => { + fetchSpy.mockResolvedValue(make402MppResponse()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [ + makeMppChallenge({ id: "a1", method: "tempo" }), + makeMppChallenge({ + id: "b2", + method: "stripe", + request: { + amount: "1000", + currency: "usd", + recipient: "acct_test", + chainId: undefined, + methodDetails: undefined, + }, + }), + ], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.mppMethods).toHaveLength(2); + expect(result.mppMethods?.[0]?.method).toBe("tempo"); + expect(result.mppMethods?.[1]?.method).toBe("stripe"); + }); + + it("MPP mppMethods includes intent, id, and request details", async () => { + fetchSpy.mockResolvedValue(make402MppResponse()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [ + makeMppChallenge({ + id: "sess-1", + intent: "session", + expires: "2026-04-01T00:00:00Z", + }), + ], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + const method = result.mppMethods?.[0]; + expect(method?.intent).toBe("session"); + expect(method?.id).toBe("sess-1"); + expect(method?.expires).toBe("2026-04-01T00:00:00Z"); + expect(method?.rawAmount).toBe("10000"); + expect(method?.chainId).toBe(4217); + }); + + it("MPP facilitator address is truncated", async () => { + fetchSpy.mockResolvedValue(make402MppResponse()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [makeMppChallenge()], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.facilitator).toBe("0x1040...6Ce2"); + }); + + it("MPP without request payload -> price undefined, network undefined", async () => { + fetchSpy.mockResolvedValue(make402MppResponse()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [ + makeMppChallenge({ request: undefined }), + ], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.protocol).toBe("mpp"); + expect(result.price).toBeUndefined(); + expect(result.network).toBeUndefined(); + expect(result.facilitator).toBeUndefined(); + }); + + it("402 with no adapter match and no MPP -> classification = 'ambiguous'", async () => { + fetchSpy.mockResolvedValue(make402Response()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ challenges: [] }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.classification).toBe("ambiguous"); + expect(result.protocol).toBeUndefined(); + }); + + it("200 with MPP www-authenticate header -> classification = 'paid'", async () => { + mockHasMppScheme.mockReturnValue(true); + fetchSpy.mockResolvedValue( + new Response("ok", { + status: 200, + headers: { + "www-authenticate": + 'Payment method="tempo", intent="charge"', + }, + }), + ); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [makeMppChallenge()], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.classification).toBe("paid"); + expect(result.protocol).toBe("mpp"); + }); + + it("GET 200 POST 402 with MPP -> postOnly = true, protocol = 'mpp'", async () => { + fetchSpy + .mockResolvedValueOnce(new Response("ok", { status: 200 })) + .mockResolvedValueOnce(make402MppResponse()); + mockedNegotiatePayment.mockResolvedValue(undefined); + mockParseMppChallenges.mockReturnValue({ + challenges: [makeMppChallenge()], + }); + + const result = await agent.diagnose("https://api.example.com/test"); + expect(result.classification).toBe("paid"); + expect(result.protocol).toBe("mpp"); + expect(result.postOnly).toBe(true); + }); +}); + +describe("classifyHealth", () => { + it("should return healthy for fast exact-scheme endpoint", () => { + expect(classifyHealth(200, "exact", "base")).toBe("healthy"); + }); + + it("should return degraded for non-exact scheme", () => { + expect(classifyHealth(100, "upto", "base")).toBe("degraded"); + }); + + it("should return degraded for slow non-stellar endpoint (>1000ms)", () => { + expect(classifyHealth(1500, "exact", "base")).toBe("degraded"); + }); + + it("should return healthy for stellar endpoint under 5000ms", () => { + expect(classifyHealth(3000, "exact", "stellar:pubnet")).toBe("healthy"); + }); + + it("should return degraded for stellar endpoint over 5000ms", () => { + expect(classifyHealth(6000, "exact", "stellar:pubnet")).toBe("degraded"); + }); + + it("should return degraded for suspicious price over $100", () => { + const expensivePrice = Money.fromDollars("150.00"); + expect(classifyHealth(100, "exact", "base", expensivePrice)).toBe("degraded"); + }); + + it("should return healthy for normal price under $100", () => { + const normalPrice = Money.fromDollars("0.05"); + expect(classifyHealth(100, "exact", "base", normalPrice)).toBe("healthy"); }); }); diff --git a/packages/sdk/tests/directory/discover.test.ts b/packages/sdk/tests/directory/discover.test.ts index cca3bb9..36b9173 100644 --- a/packages/sdk/tests/directory/discover.test.ts +++ b/packages/sdk/tests/directory/discover.test.ts @@ -51,14 +51,12 @@ import { Money } from "@boltzpay/core"; import type { DiscoveredEntry } from "../../src/directory"; import { API_DIRECTORY, - classifyProbeError, filterDirectory, sortDiscoveredEntries, toDiscoverJson, + toDiscoverStatus, withTimeout, } from "../../src/directory"; -import { NetworkError } from "../../src/errors/network-error"; -import { ProtocolError } from "../../src/errors/protocol-error"; describe("filterDirectory", () => { it("should return all entries without category", () => { @@ -80,60 +78,6 @@ describe("filterDirectory", () => { }); }); -describe("classifyProbeError", () => { - it("should classify ProtocolError protocol_detection_failed as free", () => { - const err = new ProtocolError("protocol_detection_failed", "No protocol"); - expect(classifyProbeError(err)).toEqual({ status: "free" }); - }); - - it("should classify ProtocolError payment_failed as error", () => { - const err = new ProtocolError("payment_failed", "Payment failed"); - expect(classifyProbeError(err)).toEqual({ - status: "error", - reason: "Payment failed", - }); - }); - - it("should classify NetworkError as offline", () => { - const err = new NetworkError("endpoint_unreachable", "Connection refused"); - expect(classifyProbeError(err)).toEqual({ - status: "offline", - reason: "Connection refused", - }); - }); - - it("should classify TimeoutError as offline", () => { - const err = new DOMException("Timeout", "TimeoutError"); - expect(classifyProbeError(err)).toEqual({ - status: "offline", - reason: "Timeout", - }); - }); - - it("should classify AbortError as offline", () => { - const err = new DOMException("Aborted", "AbortError"); - expect(classifyProbeError(err)).toEqual({ - status: "offline", - reason: "Aborted", - }); - }); - - it("should classify unknown error as error", () => { - const err = new Error("Something weird"); - expect(classifyProbeError(err)).toEqual({ - status: "error", - reason: "Something weird", - }); - }); - - it("should classify non-Error as error with string representation", () => { - expect(classifyProbeError("string error")).toEqual({ - status: "error", - reason: "string error", - }); - }); -}); - describe("sortDiscoveredEntries", () => { const base = { name: "Test", @@ -274,6 +218,84 @@ describe("toDiscoverJson", () => { }); }); +describe("toDiscoverStatus", () => { + const baseDiagnose = { + url: "https://test.com", + postOnly: false, + latencyMs: 100, + protocol: undefined, + formatVersion: undefined, + scheme: undefined, + network: undefined, + price: undefined, + facilitator: undefined, + } as const; + + it("should map paid classification to live status", () => { + const result = toDiscoverStatus({ + ...baseDiagnose, + classification: "paid", + isPaid: true, + protocol: "x402", + network: "eip155:8453", + price: Money.fromDollars("0.05"), + health: "healthy", + }); + expect(result).toEqual({ + status: "live", + livePrice: "$0.05", + protocol: "x402", + network: "eip155:8453", + }); + }); + + it("should map paid without price to live with unknown price", () => { + const result = toDiscoverStatus({ + ...baseDiagnose, + classification: "paid", + isPaid: true, + health: "healthy", + }); + expect(result).toEqual({ + status: "live", + livePrice: "unknown", + protocol: "unknown", + network: undefined, + }); + }); + + it("should map free_confirmed to free status", () => { + const result = toDiscoverStatus({ + ...baseDiagnose, + classification: "free_confirmed", + isPaid: false, + health: "healthy", + }); + expect(result).toEqual({ status: "free" }); + }); + + it("should map dead to offline with reason", () => { + const result = toDiscoverStatus({ + ...baseDiagnose, + classification: "dead", + deathReason: "dns_failure", + isPaid: false, + health: "dead", + }); + expect(result).toEqual({ status: "offline", reason: "dns_failure" }); + }); + + it("should map ambiguous to error", () => { + const result = toDiscoverStatus({ + ...baseDiagnose, + classification: "ambiguous", + isPaid: false, + health: "degraded", + }); + expect(result.status).toBe("error"); + }); +}); + describe("withTimeout", () => { it("should resolve when promise completes before timeout", async () => { const result = await withTimeout(Promise.resolve(42), 1000); @@ -332,27 +354,62 @@ describe("withTimeout", () => { }); describe("BoltzPay.discover()", () => { - it("should probe entries and return enriched results", async () => { - // Dynamic import to avoid circular issues; use real BoltzPay with mocked router - const { BoltzPay } = await import("../../src/boltzpay"); + it("should probe entries via diagnose and return enriched results", async () => { + const diagnoseModule = await import("../../src/diagnostics/diagnose"); + const diagnoseSpy = vi.spyOn(diagnoseModule, "diagnoseEndpoint"); + + diagnoseSpy.mockImplementation(async (input) => { + const base = { + url: input.url, + postOnly: false, + latencyMs: 50, + protocol: undefined, + formatVersion: undefined, + scheme: undefined, + network: undefined, + price: undefined, + facilitator: undefined, + } as const; + + if (input.url === "https://a.com") { + return { + ...base, + classification: "paid" as const, + isPaid: true, + protocol: "x402", + network: "eip155:8453", + price: Money.fromDollars("0.05"), + health: "healthy" as const, + }; + } + if (input.url === "https://b.com") { + return { + ...base, + classification: "free_confirmed" as const, + isPaid: false, + health: "healthy" as const, + }; + } + if (input.url === "https://c.com") { + return { + ...base, + classification: "dead" as const, + deathReason: "timeout" as const, + isPaid: false, + health: "dead" as const, + }; + } + return { + ...base, + classification: "ambiguous" as const, + isPaid: false, + health: "degraded" as const, + }; + }); + const { BoltzPay } = await import("../../src/boltzpay"); const sdk = new BoltzPay({ network: "base" }); - const quoteSpy = vi.spyOn(sdk, "quote"); - - quoteSpy.mockResolvedValueOnce({ - amount: Money.fromDollars("0.05"), - protocol: "x402", - network: "eip155:8453", - }); - quoteSpy.mockRejectedValueOnce( - new ProtocolError("protocol_detection_failed", "No protocol"), - ); - quoteSpy.mockRejectedValueOnce( - new DOMException("Timeout", "TimeoutError"), - ); - quoteSpy.mockRejectedValueOnce(new Error("Unexpected")); - // Patch getMergedDirectory to control inputs const bazaarModule = await import("../../src/bazaar"); const mergedSpy = vi.spyOn(bazaarModule, "getMergedDirectory"); const fakeEntries = [ @@ -394,7 +451,6 @@ describe("BoltzPay.discover()", () => { const results = await sdk.discover(); expect(results).toHaveLength(4); - // Sorted: live, offline (timeout), error, free expect(results[0].live.status).toBe("live"); expect(results[0].name).toBe("A"); if (results[0].live.status === "live") { @@ -412,6 +468,6 @@ describe("BoltzPay.discover()", () => { expect(results[3].name).toBe("B"); mergedSpy.mockRestore(); - quoteSpy.mockRestore(); + diagnoseSpy.mockRestore(); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0ffde4..8053fd2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,10 +19,10 @@ importers: version: link:packages/config '@commitlint/cli': specifier: ^20.4.2 - version: 20.5.0(@types/node@25.3.5)(conventional-commits-parser@6.3.0)(typescript@5.9.3) + version: 20.4.3(@types/node@25.3.5)(typescript@5.9.3) '@commitlint/config-conventional': specifier: ^20.4.2 - version: 20.5.0 + version: 20.4.3 husky: specifier: ^9.1.7 version: 9.1.7 @@ -31,7 +31,7 @@ importers: version: 0.3.18 turbo: specifier: ^2.8.9 - version: 2.8.20 + version: 2.8.15 typescript: specifier: ^5.8.3 version: 5.9.3 @@ -49,16 +49,16 @@ importers: version: 5.2.10 '@tailwindcss/vite': specifier: ^4.2.0 - version: 4.2.2(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + version: 4.2.1(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2)) '@vercel/analytics': specifier: ^1.6.1 version: 1.6.1 astro: specifier: ^5.17.2 - version: 5.18.1(@types/node@25.3.5)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.59.0)(typescript@5.9.3)(yaml@2.8.2) + version: 5.18.1(@types/node@25.3.5)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.59.0)(typescript@5.9.3)(yaml@2.8.2) tailwindcss: specifier: ^4.2.0 - version: 4.2.2 + version: 4.2.1 integrations/n8n: dependencies: @@ -71,13 +71,13 @@ importers: version: 22.19.15 n8n-workflow: specifier: ^2.0.0 - version: 2.13.0 + version: 2.11.1 typescript: specifier: ^5.8.3 version: 5.9.3 vitest: specifier: ^4.0.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) packages/ai-sdk: dependencies: @@ -102,7 +102,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) zod: specifier: ^4.3.6 version: 4.3.6 @@ -139,7 +139,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) packages/config: {} @@ -156,7 +156,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) packages/mcp: dependencies: @@ -187,7 +187,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) packages/protocols: dependencies: @@ -202,7 +202,7 @@ importers: version: 7.0.0(typescript@5.9.3) '@solana/kit': specifier: ^6.1.0 - version: 6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + version: 6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) '@x402/core': specifier: ^2.6.0 version: 2.6.0 @@ -211,10 +211,10 @@ importers: version: 2.6.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10) '@x402/fetch': specifier: ^2.6.0 - version: 2.7.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + version: 2.6.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) '@x402/svm': specifier: ^2.6.0 - version: 2.6.0(@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))(@solana/sysvars@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) + version: 2.6.0(@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))(@solana/sysvars@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) async-mutex: specifier: ^0.5.0 version: 0.5.0 @@ -225,6 +225,9 @@ importers: '@boltzpay/config': specifier: workspace:* version: link:../config + mppx: + specifier: 0.4.7 + version: 0.4.7(@modelcontextprotocol/sdk@1.27.1(zod@3.25.76))(express@5.2.1)(hono@4.12.6)(openapi-types@12.1.3)(typescript@5.9.3)(viem@2.47.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)) tsup: specifier: ^8.5.1 version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(typescript@5.9.3)(yaml@2.8.2) @@ -233,7 +236,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) packages/sdk: dependencies: @@ -255,7 +258,7 @@ importers: version: 1.44.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) '@hono/node-server': specifier: ^1.19.9 - version: 1.19.11(hono@4.12.8) + version: 1.19.11(hono@4.12.6) '@types/node': specifier: ^22.15.0 version: 22.19.15 @@ -267,10 +270,10 @@ importers: version: 2.6.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10) '@x402/hono': specifier: ^2.6.0 - version: 2.7.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(hono@4.12.8)(typescript@5.9.3)(utf-8-validate@5.0.10) + version: 2.6.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(hono@4.12.6)(typescript@5.9.3)(utf-8-validate@5.0.10) hono: specifier: ^4.11.9 - version: 4.12.8 + version: 4.12.6 tsup: specifier: ^8.5.1 version: 8.5.1(jiti@2.6.1)(postcss@8.5.8)(typescript@5.9.3)(yaml@2.8.2) @@ -279,7 +282,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) packages: @@ -308,6 +311,12 @@ packages: '@andrewbranch/untar.js@1.0.3': resolution: {integrity: sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==} + '@apidevtools/json-schema-ref-parser@14.2.1': + resolution: {integrity: sha512-HmdFw9CDYqM6B25pqGBpNeLCKvGPlIx1EbLrVL0zPvj50CJQUHyBNBw45Muk0kEIkogo1VZvOKHajdMuAzSxRg==} + engines: {node: '>= 20'} + peerDependencies: + '@types/json-schema': ^7.0.15 + '@arethetypeswrong/cli@0.18.2': resolution: {integrity: sha512-PcFM20JNlevEDKBg4Re29Rtv2xvjvQZzg7ENnrWFSS0PHgdP2njibVFw+dRUhNkPgNfac9iUqO0ohAXqQL4hbw==} engines: {node: '>=20'} @@ -346,8 +355,8 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} hasBin: true @@ -430,61 +439,61 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - '@commitlint/cli@20.5.0': - resolution: {integrity: sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==} + '@commitlint/cli@20.4.3': + resolution: {integrity: sha512-Z37EMoDT7+Upg500vlr/vZrgRsb6Xc5JAA3Tv7BYbobnN/ZpqUeZnSLggBg2+1O+NptRDtyujr2DD1CPV2qwhA==} engines: {node: '>=v18'} hasBin: true - '@commitlint/config-conventional@20.5.0': - resolution: {integrity: sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==} + '@commitlint/config-conventional@20.4.3': + resolution: {integrity: sha512-9RtLySbYQAs8yEqWEqhSZo9nYhbm57jx7qHXtgRmv/nmeQIjjMcwf6Dl+y5UZcGWgWx435TAYBURONaJIuCjWg==} engines: {node: '>=v18'} - '@commitlint/config-validator@20.5.0': - resolution: {integrity: sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==} + '@commitlint/config-validator@20.4.3': + resolution: {integrity: sha512-jCZpZFkcSL3ZEdL5zgUzFRdytv3xPo8iukTe9VA+QGus/BGhpp1xXSVu2B006GLLb2gYUAEGEqv64kTlpZNgmA==} engines: {node: '>=v18'} - '@commitlint/ensure@20.5.0': - resolution: {integrity: sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==} + '@commitlint/ensure@20.4.3': + resolution: {integrity: sha512-WcXGKBNn0wBKpX8VlXgxqedyrLxedIlLBCMvdamLnJFEbUGJ9JZmBVx4vhLV3ZyA8uONGOb+CzW0Y9HDbQ+ONQ==} engines: {node: '>=v18'} '@commitlint/execute-rule@20.0.0': resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} engines: {node: '>=v18'} - '@commitlint/format@20.5.0': - resolution: {integrity: sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==} + '@commitlint/format@20.4.3': + resolution: {integrity: sha512-UDJVErjLbNghop6j111rsHJYGw6MjCKAi95K0GT2yf4eeiDHy3JDRLWYWEjIaFgO+r+dQSkuqgJ1CdMTtrvHsA==} engines: {node: '>=v18'} - '@commitlint/is-ignored@20.5.0': - resolution: {integrity: sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==} + '@commitlint/is-ignored@20.4.3': + resolution: {integrity: sha512-W5VQKZ7fdJ1X3Tko+h87YZaqRMGN1KvQKXyCM8xFdxzMIf1KCZgN4uLz3osLB1zsFcVS4ZswHY64LI26/9ACag==} engines: {node: '>=v18'} - '@commitlint/lint@20.5.0': - resolution: {integrity: sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==} + '@commitlint/lint@20.4.3': + resolution: {integrity: sha512-CYOXL23e+nRKij81+d0+dymtIi7Owl9QzvblJYbEfInON/4MaETNSLFDI74LDu+YJ0ML5HZyw9Vhp9QpckwQ0A==} engines: {node: '>=v18'} - '@commitlint/load@20.5.0': - resolution: {integrity: sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==} + '@commitlint/load@20.4.3': + resolution: {integrity: sha512-3cdJOUVP+VcgHa7bhJoWS+Z8mBNXB5aLWMBu7Q7uX8PSeWDzdbrBlR33J1MGGf7r1PZDp+mPPiFktk031PgdRw==} engines: {node: '>=v18'} '@commitlint/message@20.4.3': resolution: {integrity: sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==} engines: {node: '>=v18'} - '@commitlint/parse@20.5.0': - resolution: {integrity: sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==} + '@commitlint/parse@20.4.3': + resolution: {integrity: sha512-hzC3JCo3zs3VkQ833KnGVuWjWIzR72BWZWjQM7tY/7dfKreKAm7fEsy71tIFCRtxf2RtMP2d3RLF1U9yhFSccA==} engines: {node: '>=v18'} - '@commitlint/read@20.5.0': - resolution: {integrity: sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==} + '@commitlint/read@20.4.3': + resolution: {integrity: sha512-j42OWv3L31WfnP8WquVjHZRt03w50Y/gEE8FAyih7GQTrIv2+pZ6VZ6pWLD/ml/3PO+RV2SPtRtTp/MvlTb8rQ==} engines: {node: '>=v18'} - '@commitlint/resolve-extends@20.5.0': - resolution: {integrity: sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==} + '@commitlint/resolve-extends@20.4.3': + resolution: {integrity: sha512-QucxcOy+00FhS9s4Uy0OyS5HeUV+hbC6OLqkTSIm6fwMdKva+OEavaCDuLtgd9akZZlsUo//XzSmPP3sLKBPog==} engines: {node: '>=v18'} - '@commitlint/rules@20.5.0': - resolution: {integrity: sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==} + '@commitlint/rules@20.4.3': + resolution: {integrity: sha512-Yuosd7Grn5qiT7FovngXLyRXTMUbj9PYiSkvUgWK1B5a7+ZvrbWDS7epeUapYNYatCy/KTpPFPbgLUdE+MUrBg==} engines: {node: '>=v18'} '@commitlint/to-lines@20.0.0': @@ -495,24 +504,12 @@ packages: resolution: {integrity: sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==} engines: {node: '>=v18'} - '@commitlint/types@20.5.0': - resolution: {integrity: sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==} + '@commitlint/types@20.4.3': + resolution: {integrity: sha512-51OWa1Gi6ODOasPmfJPq6js4pZoomima4XLZZCrkldaH2V5Nb3bVhNXPeT6XV0gubbainSpTw4zi68NqAeCNCg==} engines: {node: '>=v18'} - '@conventional-changelog/git-client@2.6.0': - resolution: {integrity: sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==} - engines: {node: '>=18'} - peerDependencies: - conventional-commits-filter: ^5.0.0 - conventional-commits-parser: ^6.3.0 - peerDependenciesMeta: - conventional-commits-filter: - optional: true - conventional-commits-parser: - optional: true - - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/runtime@1.9.1': + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} @@ -1005,6 +1002,10 @@ packages: peerDependencies: hono: ^4 + '@humanwhocodes/momoa@2.0.4': + resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} + engines: {node: '>=10.10.0'} + '@img/colour@1.1.0': resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} @@ -1190,9 +1191,6 @@ packages: '@n8n/errors@0.6.0': resolution: {integrity: sha512-oVJ0lgRYJY6/aPOW2h37ea5T+nX7/wULRn5FymwYeaiYlsLdqwIQEtGwZrajpzxJB0Os74u4lSH3WWQgZCkgxQ==} - '@n8n/expression-runtime@0.5.0': - resolution: {integrity: sha512-DqkF79FyuljdjlSz1i6PfbrQ47w5U/fIqjoyAbRVxr+YatiM3RAe0zhfQ1ur1lV5QTq0BHnY8zsfFQ+DIItPhA==} - '@n8n/tournament@1.0.6': resolution: {integrity: sha512-UGSxYXXVuOX0yL6HTLBStKYwLIa0+JmRKiSZSCMcM2s2Wax984KWT6XIA1TR/27i7yYpDk1MY14KsTPnuEp27A==} engines: {node: '>=20.15', pnpm: '>=9.5'} @@ -1246,6 +1244,31 @@ packages: resolution: {integrity: sha512-HDVTWq3H0uTXiU0eeSQntcVUTPP3GamzeXI41+x7uU9J65JgWQh3qWZHblR1i0npXfFtF+mxBiU2nJH8znxWnQ==} engines: {node: '>=18'} + '@readme/better-ajv-errors@2.4.0': + resolution: {integrity: sha512-9WODaOAKSl/mU+MYNZ2aHCrkoRSvmQ+1YkLj589OEqqjOAhbn8j7Z+ilYoiTu/he6X63/clsxxAB4qny9/dDzg==} + engines: {node: '>=18'} + peerDependencies: + ajv: 4.11.8 - 8 + + '@readme/openapi-parser@6.0.0': + resolution: {integrity: sha512-PaTnrKlKgEJZzjJ77AAhGe28NiyLBdiKMx95rJ9xlLZ8QLqYitMpPBQAKhsuEGOWQQbsIMfBZEPavbXghACQHA==} + engines: {node: '>=20'} + peerDependencies: + openapi-types: '>=7' + + '@readme/openapi-schemas@3.1.0': + resolution: {integrity: sha512-9FC/6ho8uFa8fV50+FPy/ngWN53jaUu4GRXlAjcxIRrzhltJnpKkBG2Tp0IDraFJeWrOpk84RJ9EMEEYzaI1Bw==} + engines: {node: '>=18'} + + '@remix-run/fetch-proxy@0.7.1': + resolution: {integrity: sha512-rPLfOpAaCXtm1dLI45uIPKERNbXbrh0P9AJc1sliz8pWd/McaFYjdr5KzB4QrFSfPvEt/Wmy6F2521qB1kK0ug==} + + '@remix-run/headers@0.19.0': + resolution: {integrity: sha512-+62NbkXuXm9r/NdG6KfH9OCKofCWm8VjkrVPICiHKtRl8Gf2Vi6eFTN4mGgBlZRhd5mmEVRV4hTIn/JUSHDAOw==} + + '@remix-run/node-fetch-server@0.13.0': + resolution: {integrity: sha512-1EsNo0ZpgXu/90AWoRZf/oE3RVTUS80tiTUpt+hv5pjtAkw7icN4WskDwz/KdAw5ARbJLMhZBrO1NqThmy/McA==} + '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -1573,10 +1596,6 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - '@simple-libs/child-process-utils@1.0.2': - resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==} - engines: {node: '>=18'} - '@simple-libs/stream-utils@1.2.0': resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} engines: {node: '>=18'} @@ -1615,8 +1634,8 @@ packages: typescript: optional: true - '@solana/accounts@6.5.0': - resolution: {integrity: sha512-h3zQFjwZjmy+YxgTGOEna6g74Tsn4hTBaBCslwPT4QjqWhywe2JrM2Ab0ANfJcj7g/xrHF5QJ/FnUIcyUTeVfQ==} + '@solana/accounts@6.2.0': + resolution: {integrity: sha512-6XfdN44nqibyxZDUyJ6o8Jmrh7y3E8Vu02PAuKBlld8mszPBqikKXloZEyjRPjnjpBStepvulOmgmHcmlWYCWw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1633,8 +1652,8 @@ packages: typescript: optional: true - '@solana/addresses@6.5.0': - resolution: {integrity: sha512-iD4/u3CWchQcPofbwzteaE9RnFJSoi654Rnhru5fOu6U2XOte3+7t50d6OxdxQ109ho2LqZyVtyCo2Wb7u1aJQ==} + '@solana/addresses@6.2.0': + resolution: {integrity: sha512-IC0vkLZqPgM/3ugqlLRVIZ/QXPwLZT8jMnEP7KjeIVrLBGIS7tJpBWJuWMjuTjBGSVsKEC3aZgPA4CMY5kJ7lA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1651,8 +1670,8 @@ packages: typescript: optional: true - '@solana/assertions@6.5.0': - resolution: {integrity: sha512-rEAf40TtC9r6EtJFLe39WID4xnTNT6hdOVRfD1xDzmIQdVOyGgIbJGt2FAuB/uQDKLWneWMnvGDBim+K61Bljw==} + '@solana/assertions@6.2.0': + resolution: {integrity: sha512-8T4tsyGnCpRz+zKSciDtUe5l1r8qCu3cXOb2njSsLAi1k1izz+laJd4nBtbnhoHCCP88mgWOIMZj5uZ+GXXMyA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1679,8 +1698,8 @@ packages: typescript: optional: true - '@solana/codecs-core@6.5.0': - resolution: {integrity: sha512-Wb+YUj7vUKz5CxqZkrkugtQjxOP2fkMKnffySRlAmVAkpRnQvBY/2eP3VJAKTgDD4ru9xHSIQSpDu09hC/cQZg==} + '@solana/codecs-core@6.2.0': + resolution: {integrity: sha512-HTStXi9t07g3A8PHeNIaa+BYVfqxYXn9WEpuOpSV7XFoahlxGcryChbm59VtOzb3a8tSaVL/1yd5hCbe+WwI8g==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1697,8 +1716,8 @@ packages: typescript: optional: true - '@solana/codecs-data-structures@6.5.0': - resolution: {integrity: sha512-Rxi5zVJ1YA+E6FoSQ7RHP+3DF4U7ski0mJ3H5CsYQP24QLRlBqWB3X6m2n9GHT5O3s49UR0sqeF4oyq0lF8bKw==} + '@solana/codecs-data-structures@6.2.0': + resolution: {integrity: sha512-w2pnl/nTK34e4+zs6DBdfkZdaEe9nk24xNjmeC7T0lN/mQiawO2uYgJ/I0bQg1J7qMZBN9UsasbSTWfnWQrVOQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1721,8 +1740,8 @@ packages: typescript: optional: true - '@solana/codecs-numbers@6.5.0': - resolution: {integrity: sha512-gU/7eYqD+zl2Kwzo7ctt7YHaxF+c3RX164F+iU4X02dwq8DGVcypp+kmEF1QaO6OiShtdryTxhL+JJmEBjhdfA==} + '@solana/codecs-numbers@6.2.0': + resolution: {integrity: sha512-4bA0eWxY5bZ9N3MNFxZIvd7N+qIHoEemIg5o/UC2d8pgIBx4zwyyvy3p9a7Mfnj+s+Iia3HbnVl7kYcakuFeBw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1742,8 +1761,8 @@ packages: typescript: optional: true - '@solana/codecs-strings@6.5.0': - resolution: {integrity: sha512-9TuQQxumA9gWJeJzbv1GUg0+o0nZp204EijX3efR+lgBOKbkU7W0UWp33ygAZ+RvWE+kTs48ePoYoJ7UHpyxkQ==} + '@solana/codecs-strings@6.2.0': + resolution: {integrity: sha512-y7OY5jGDqHlEi4IIfxWnHocRrjarjUujnu56cCYmK1MVgGa3qmLxpSIzPPJlHQiTBLP/iLeVjvQjF8MWOMZSiw==} engines: {node: '>=20.18.0'} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 @@ -1763,8 +1782,8 @@ packages: typescript: optional: true - '@solana/codecs@6.5.0': - resolution: {integrity: sha512-WfqMqUXk4jcCJQ9nfKqjDcCJN2Pt8/AKe/E78z8OcblFGVJnTzcu2yZpE2gsqM+DJyCVKdQmOY+NS8Uckk5e5w==} + '@solana/codecs@6.2.0': + resolution: {integrity: sha512-vIHbT/qo6A0Go4j87RpsM9eDJQXK1mmGkWq5/6Q73j/xerjZxa7cFif0GMVCmORoyLZJ1LoGICRpqaZYxKSP1Q==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1789,8 +1808,8 @@ packages: typescript: optional: true - '@solana/errors@6.5.0': - resolution: {integrity: sha512-XPc0I8Ck6vgx8Uu+LVLewx/1RWDkXkY3lU+1aN1kmbrPAQWbX4Txk7GPmuIIFpyys8o5aKocYfNxJOPKvfaQhg==} + '@solana/errors@6.2.0': + resolution: {integrity: sha512-GckKPJY+0AfIWHtVnccQFjpCXgIxz12RVDOgCJa7Nc/EcxisOGpTqgPYnZ4Q16jOuBI5dgeRxYNGBdyJJgWy3g==} engines: {node: '>=20.18.0'} hasBin: true peerDependencies: @@ -1808,8 +1827,8 @@ packages: typescript: optional: true - '@solana/fast-stable-stringify@6.5.0': - resolution: {integrity: sha512-5ATQDwBVZMoenX5KS23uFswtaAGoaZB9TthzUXle3tkU3tOfgQTuEWEoqEBYc7ct0sK6LtyE1XXT/NP5YvAkkQ==} + '@solana/fast-stable-stringify@6.2.0': + resolution: {integrity: sha512-Bt8OU0HNdqh0Dr9lTgiBwKAl5AUpXk5TD18hHX1jtGhuFOsRvRdPLa59D/32x/gaxls/03kaXDVKZ60MCdf5VQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1826,8 +1845,8 @@ packages: typescript: optional: true - '@solana/functional@6.5.0': - resolution: {integrity: sha512-/KYgY7ZpBJfkN8+qlIvxuBpxv32U9jHXIOOJh3U5xk8Ncsa9Ex5VwbU9NkOf43MJjoIamsP0vARCHjcqJwe5JQ==} + '@solana/functional@6.2.0': + resolution: {integrity: sha512-vurCNEtWx/kQuO7yuQZGftZWTJAehwNuL6V/v7ZmA11i7OwUBs9W7RYzkP/vr0De1uGn96xsTbN9p/EbsF45Gw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1844,8 +1863,8 @@ packages: typescript: optional: true - '@solana/instruction-plans@6.5.0': - resolution: {integrity: sha512-zp2asevpyMwvhajHYM1aruYpO+xf3LSwHEI2FK6E2hddYZaEhuBy+bz+NZ1ixCyfx3iXcq7MamlFQc2ySHDyUQ==} + '@solana/instruction-plans@6.2.0': + resolution: {integrity: sha512-XGt7iclH4HFFH9Jrct2Te23y06nuuk2YD1fJRuR21nnb74cAeXJi6+PCK6zNCINTR8l9CxKR3pzWDq6BD+jwXg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1862,8 +1881,8 @@ packages: typescript: optional: true - '@solana/instructions@6.5.0': - resolution: {integrity: sha512-2mQP/1qqr5PCfaVMzs9KofBjpyS7J1sBV6PidGoX9Dg5/4UgwJJ+7yfCVQPn37l1nKCShm4I+pQAy5vbmrxJmA==} + '@solana/instructions@6.2.0': + resolution: {integrity: sha512-eBYE4ucmJ5Xc7w4UJek0/GbcbzJoWWk/aolDAt9xkea0lDxSC+y3fW58CvBc9vz1qxnEcFrxa+pgUBqwZ1BIFg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1880,8 +1899,8 @@ packages: typescript: optional: true - '@solana/keys@6.5.0': - resolution: {integrity: sha512-CN5jmodX9j5CZKrWLM5XGaRlrLl/Ebl4vgqDXrnwC2NiSfUslLsthuORMuVUTDqkzBX/jd/tgVXFRH2NYNzREQ==} + '@solana/keys@6.2.0': + resolution: {integrity: sha512-1CE14VNpB3DIQNtOdrvYUuDTDcdtRXnnM9dAhvTeHbQeDa30sWFBqBL2dVDIVP8F/UosX8fEzMR9SQvI19IivQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1898,8 +1917,8 @@ packages: typescript: optional: true - '@solana/kit@6.5.0': - resolution: {integrity: sha512-4ysrtqMRd7CTYRv179gQq4kbw9zMsJCLhWjiyOmLZ4co4ld3L654D8ykW7yqWE5PJwF0hzEfheE7oBscO37nvw==} + '@solana/kit@6.2.0': + resolution: {integrity: sha512-T9fRFHAFZ8CMtetpukPhCB9HoumuoOYTgCXrry5G2yhTO86a5PMkIwZI+kya6iDh0Qp9srOjtuGrVh4xA+BLFQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1916,8 +1935,8 @@ packages: typescript: optional: true - '@solana/nominal-types@6.5.0': - resolution: {integrity: sha512-HngIM2nlaDPXk0EDX0PklFqpjGDKuOFnlEKS0bfr2F9CorFwiNhNjhb9lPH+FdgsogD1wJ8wgLMMk1LZWn5kgQ==} + '@solana/nominal-types@6.2.0': + resolution: {integrity: sha512-HkrXkM8Ku4J0XYfh0XUEo67IyX1BAfI7m4MpJvnXh987YeiSwoyGTLBxQAWFIONxuTDTR/s5mfNXFB+6uAQTlA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1934,8 +1953,8 @@ packages: typescript: optional: true - '@solana/offchain-messages@6.5.0': - resolution: {integrity: sha512-IYuidJCwfXg5xlh3rkflkA1fbTKWTsip8MdI+znvXm87grfqOYCTd6t/SKiV4BhLl/65Tn0wB/zvZ1cmzJqa1w==} + '@solana/offchain-messages@6.2.0': + resolution: {integrity: sha512-Lx3yR2+t0uWULghEO7sMZR1xmPsZz/BvRPc9JiRjTd3jP54x346vecXZCIRdFXHZ79KVeCGaCx0GC8d41MGmSQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1952,8 +1971,8 @@ packages: typescript: optional: true - '@solana/options@6.5.0': - resolution: {integrity: sha512-jdZjSKGCQpsMFK+3CiUEI7W9iGsndi46R4Abk66ULNLDoMsjvfqNy8kqktm0TN0++EX8dKEecpFwxFaA4VlY5g==} + '@solana/options@6.2.0': + resolution: {integrity: sha512-0He7qDNyFt61GwjLHdKM5uKsHYEKgiLljbysyZCBrr7dp42LkVmd59HA+bLBcnijcgJZ6JsNjxHTBOw+esulKQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1970,8 +1989,8 @@ packages: typescript: optional: true - '@solana/plugin-core@6.5.0': - resolution: {integrity: sha512-L6N69oNQOAqljH4GnLTaxpwJB0nibW9DrybHZxpGWshyv6b/EvwvkDVRKj5bNqtCG+HRZUHnEhLi1UgZVNkjpQ==} + '@solana/plugin-core@6.2.0': + resolution: {integrity: sha512-muQKHsFs2yp6w9keIDwv8b1mALHjHMUpBM1HRxKZML9o4BLIj89gsvrCT5QaHVjIcScOWvtf8D/EQ2SUHwIpMg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1979,8 +1998,8 @@ packages: typescript: optional: true - '@solana/plugin-interfaces@6.5.0': - resolution: {integrity: sha512-/ZlybbMaR7P4ySersOe1huioMADWze0AzsHbzgkpt5dJUv2tz5cpaKdu7TEVQkUZAFhLdqXQULNGqAU5neOgzg==} + '@solana/plugin-interfaces@6.2.0': + resolution: {integrity: sha512-yh5RflQPE67ub30ssNuOXUGWk0rzkuI3ybjF5AX6p49BJWfUmx3j8GCYcTjRb9tCzsCzONlP73U9uGrY0zzFvQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -1988,8 +2007,8 @@ packages: typescript: optional: true - '@solana/program-client-core@6.5.0': - resolution: {integrity: sha512-eUz1xSeDKySGIjToAryPmlESdj8KX0Np7R+Pjt+kSFGw5Jgmn/Inh4o8luoeEnf5XwbvSPVb4aHpIsDyoUVbIg==} + '@solana/program-client-core@6.2.0': + resolution: {integrity: sha512-B044ZU02akGs1AtggEUvg6fsNKL970UJVRDDKBvQ6M9GX7c7R6sbNPFQoAuKUW3dAa9KSI5w2vOJDJTLzn1B+Q==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2006,8 +2025,8 @@ packages: typescript: optional: true - '@solana/programs@6.5.0': - resolution: {integrity: sha512-srn3nEROBxCnBpVz/bvLkVln1BZtk3bS3nuReu3yaeOLkKl8b0h1Zp0YmXVyXHzdMcYahsTvKKLR1ZtLZEyEPA==} + '@solana/programs@6.2.0': + resolution: {integrity: sha512-8GEmlY3d20UI3ncHvu2IDk5U+lvdMD8888oRYqK4YOlsumDGgIjN6RFHL+0KvkMx7k1yU7dpUl9/WpUj2CioEQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2024,8 +2043,8 @@ packages: typescript: optional: true - '@solana/promises@6.5.0': - resolution: {integrity: sha512-n5rsA3YwOO2nUst6ghuVw6RSnuZQYqevqBKqVYbw11Z4XezsoQ6hb78opW3J9YNYapw9wLWy6tEfUsJjY+xtGw==} + '@solana/promises@6.2.0': + resolution: {integrity: sha512-m4JJtbnd13ifYQn7Dx3ZqMmV8/yJ59QeGb9hDdQRWAtJs40JocnlanKGZ0yX0FxYq5mHMWDR6P9Yz/Kh3XPQ+Q==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2042,8 +2061,8 @@ packages: typescript: optional: true - '@solana/rpc-api@6.5.0': - resolution: {integrity: sha512-b+kftroO8vZFzLHj7Nk/uATS3HOlBUsUqdGg3eTQrW1pFgkyq5yIoEYHeFF7ApUN/SJLTK86U8ofCaXabd2SXA==} + '@solana/rpc-api@6.2.0': + resolution: {integrity: sha512-jl40NKwpHXkCje1HAyBr2iiIhYl3G5CuUhOZeD9uikBE0lIef2vrqj5qClUkrQgqYOCpb/Kh0QFdKM4PI+HYqg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2060,8 +2079,8 @@ packages: typescript: optional: true - '@solana/rpc-parsed-types@6.5.0': - resolution: {integrity: sha512-129c8meL6CxRg56/HfhkFOpwYteQH9Rt0wyXOXZQx3a3FNpcJLd4JdPvxDsLBE3EupEkXLGVku/1bGKz+F2J+g==} + '@solana/rpc-parsed-types@6.2.0': + resolution: {integrity: sha512-NzhrZ7ENoqtlw3Xy9gmaE6ByVC38BSTL752O7iXTZF0vUZY7aY+mMmjvnGR5PCPr1UM/GI29KD+gXQXk8NJOOA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2078,8 +2097,8 @@ packages: typescript: optional: true - '@solana/rpc-spec-types@6.5.0': - resolution: {integrity: sha512-XasJp+sOW6PLfNoalzoLnm+j3LEZF8XOQmSrOqv9AGrGxQckkuOf6iXZucWTqeNKdstsOpU28BN2B6qOavfRzQ==} + '@solana/rpc-spec-types@6.2.0': + resolution: {integrity: sha512-aWyv7BMayXwKHpxc70Y24AGouvvRcBPkgNGtcPbfxnlyJIdFhqkWyHyuSzKNJa6jKLxOfb7r9CB9YkkiNU7q2Q==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2096,8 +2115,8 @@ packages: typescript: optional: true - '@solana/rpc-spec@6.5.0': - resolution: {integrity: sha512-k4O7Kg0QfVyjUqQovL+WZJ1iuPzq0jiUDcWYgvzFjYVxQDVOIZmAol7yTvLEL4maVmf0tNFDsrDaB6t75MKRZA==} + '@solana/rpc-spec@6.2.0': + resolution: {integrity: sha512-oITJE++fknwR4i1wfvkPlYs6U2NZoTOCLpKa7OBwOr/rHrStfgmvCA57pzEiBuepcWQW34oncCw2eOxMwl8paQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2114,8 +2133,8 @@ packages: typescript: optional: true - '@solana/rpc-subscriptions-api@6.5.0': - resolution: {integrity: sha512-smqNjT2C5Vf9nWGIwiYOLOP744gRWKi2i2g0i3ZVdsfoouvB0d/WTQ2bbWq47MrdV8FSuGnjAOM3dRIwYmYOWw==} + '@solana/rpc-subscriptions-api@6.2.0': + resolution: {integrity: sha512-AVV970+DfqwgtQbTliZ6ab/P90OLrh/Pa2hTJ/lAIvyj9sdGYtWOcxOY5ql4/NM80vYPLS4Jgk/x1RKbcgXc+Q==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2132,8 +2151,8 @@ packages: typescript: optional: true - '@solana/rpc-subscriptions-channel-websocket@6.5.0': - resolution: {integrity: sha512-xRKH3ZwIoV9Zua9Gp0RR0eL8lXNgx+iNIkE3F0ROlOzI48lt4lRJ7jLrHQCN3raVtkatFVuEyZ7e9eLHK9zhAw==} + '@solana/rpc-subscriptions-channel-websocket@6.2.0': + resolution: {integrity: sha512-e9elkohNqaz522/4GcF7i7OLVOn4hzzpQCdvQ6gkO2AkoG2v9AxbfnPz4OedSNG8JmciAQ9HpioubyVFGokoew==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2150,8 +2169,8 @@ packages: typescript: optional: true - '@solana/rpc-subscriptions-spec@6.5.0': - resolution: {integrity: sha512-Mi8g9rNS2lG7lyNkDhOVfQVfDC7hXKgH+BlI5qKGk+8cfyU7VDq6tVjDysu6kBWGOPHZxyCvcL6+xW/EkdVoAg==} + '@solana/rpc-subscriptions-spec@6.2.0': + resolution: {integrity: sha512-ggFjwQj9tqXBGnNaOHwt/cw1CXuo5JTuzBnIij6jVQq26MZ3+VDib6ELaR8zlRxwVitYQzRmDDcOyISTfEHlfw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2168,8 +2187,8 @@ packages: typescript: optional: true - '@solana/rpc-subscriptions@6.5.0': - resolution: {integrity: sha512-EenogPQw9Iy8VUj8anu7xoBnPk7gu1J6sAi4MTVlNVz02sNjdUBJoSS0PRJZuhSM1ktPTtHrNwqlXP8TxPR7jg==} + '@solana/rpc-subscriptions@6.2.0': + resolution: {integrity: sha512-it9q5XNQxAZCBkJU1I6Q6Cpf579pdymKeHJUAVTsGmtM+K3TvOJQxFt7TDqxa12bpCD3rxYH1UClnK0VbaSk5A==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2186,8 +2205,8 @@ packages: typescript: optional: true - '@solana/rpc-transformers@6.5.0': - resolution: {integrity: sha512-kS0d+LuuSLfsod2cm2xp0mNj65PL1aomwu6VKtubmsdESwPXHIaI9XrpkPCBuhNSz1SwVp4OkfK5O/VOOHYHSw==} + '@solana/rpc-transformers@6.2.0': + resolution: {integrity: sha512-d6lrvaTDmKwkrWBUHJqwlisbTQ4akhGsCTO5F8NCgqEUADPKVnPrfvhsdtqYSw7JqB5LcEUawt6eZ1ZR/nJokA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2204,8 +2223,8 @@ packages: typescript: optional: true - '@solana/rpc-transport-http@6.5.0': - resolution: {integrity: sha512-A3qgDGiUIHdtAfc2OyazlQa7IvRh+xyl0dmzaZlz4rY7Oc7Xk8jmXtaKGkgXihLyAK3oVSqSz5gn9yEfx55eXA==} + '@solana/rpc-transport-http@6.2.0': + resolution: {integrity: sha512-jf1yJi1v1Zi+n8WaZbtqNqV/h/XOTdC1nEwwm0kfRQiqNGHoRQFd+8Id/jUO6CL3TVYy1B1aQgH6Wh/3cr9QJA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2222,8 +2241,8 @@ packages: typescript: optional: true - '@solana/rpc-types@6.5.0': - resolution: {integrity: sha512-hxts27+Z2VNv4IjXGcXkqbj/MgrN9Xtw/4iE1qZk68T2OAb5vA4b8LHchsOHmHvrzZfo8XDvB9mModCdM3JPsQ==} + '@solana/rpc-types@6.2.0': + resolution: {integrity: sha512-T9Zm/a5wW8kMtA6+M73C2PBknnWtGESfiIkL5qIaWoUbMJVZTK+zlibbr0R+b1+3UcRZWzHz4aIceacAtZiioQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2240,8 +2259,8 @@ packages: typescript: optional: true - '@solana/rpc@6.5.0': - resolution: {integrity: sha512-lGj7ZMVOR3Rf16aByXD6ghrMqw3G8rAMuWCHU4uMKES5M5VLqNv6o71bSyoTxVMGrmYdbALOvCbFMFINAxtoBg==} + '@solana/rpc@6.2.0': + resolution: {integrity: sha512-xvCdVzZuQOWRvLB7vsHyhuHpPjFqywyJmGT48y0m35VMKw9u5xEWhpXZ2zV4GypyGLUQztlCPN83YGkcUqCeHA==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2258,8 +2277,8 @@ packages: typescript: optional: true - '@solana/signers@6.5.0': - resolution: {integrity: sha512-AL75/DyDUhc+QQ+VGZT7aRwJNzIUTWvmLNXQRlCVhLRuyroXzZEL2WJBs8xOwbZXjY8weacfYT7UNM8qK6ucDg==} + '@solana/signers@6.2.0': + resolution: {integrity: sha512-1HSa7qlbpjIcb7uhYcOwJzlH4niUu5W4X167nVeNbLA6yaqBABAM8su6iqdetnenv2Vyx2jB32WGbECKRB9NGQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2276,8 +2295,8 @@ packages: typescript: optional: true - '@solana/subscribable@6.5.0': - resolution: {integrity: sha512-Jmy2NYmQN68FsQzKJ5CY3qrxXBJdb5qtJKp8B4byPPO5liKNIsC59HpT0Tq8MCNSfBMmOkWF2rrVot2/g1iB1A==} + '@solana/subscribable@6.2.0': + resolution: {integrity: sha512-HnQmydL541gGu6wbwH3KMdd17FxDSKp04yMPwzpYKZtOGGA4yF5F8xT2yKRmy9oL6Ib1esYHUF2Fkh+EinjgmQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2294,8 +2313,8 @@ packages: typescript: optional: true - '@solana/sysvars@6.5.0': - resolution: {integrity: sha512-iLSS5qj0MWNiGH1LN1E4jhGsXH9D3tWSjwaB6zK9LjhLdVYcPfkosBkj7s0EHHrH03QlwiuFdU0Y2kH8Jcp8kw==} + '@solana/sysvars@6.2.0': + resolution: {integrity: sha512-Rug/KbW+vSMtKeMmC7EK/PAigb1IX9V6sWj4M6VDV+VhVek5LbFQu1breckKwDaHdsQWQQSzLYggBMBFyirRLQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2312,8 +2331,8 @@ packages: typescript: optional: true - '@solana/transaction-confirmation@6.5.0': - resolution: {integrity: sha512-hfdRBq4toZj7DRMgBN3F0VtJpmTAEtcVTTDZoiszoSpSVa2cAvFth6KypIqASVFZyi9t4FKolLP8ASd3/39UQg==} + '@solana/transaction-confirmation@6.2.0': + resolution: {integrity: sha512-HyqXUZt5RU2MNAC/odxxK73Q4hibXErY+oLaF5hIPVbKeuZ6smfuDmz2b66Hcwlveko9he5MtmLhuL8j8e4QnQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2330,8 +2349,8 @@ packages: typescript: optional: true - '@solana/transaction-messages@6.5.0': - resolution: {integrity: sha512-ueXkm5xaRlqYBFAlABhaCKK/DuzIYSot0FybwSDeOQCDy2hvU9Zda16Iwa1n56M0fG+XUvFJz2woG3u9DhQh1g==} + '@solana/transaction-messages@6.2.0': + resolution: {integrity: sha512-ccKLHoNWU/ZknOqTAAbvd9LJDQfHnT5SZfPbrYNlpYnjK7ILaUlqq2FG/PUK2Y4SWStAyub0myDy4P0s21G9mg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2348,8 +2367,8 @@ packages: typescript: optional: true - '@solana/transactions@6.5.0': - resolution: {integrity: sha512-b3eJrrGmwpk64VLHjOrmXKAahPpba42WX/FqSUn4WRXPoQjga7Mb57yp+EaRVeQfjszKCkF+13yu+ni6iv2NFQ==} + '@solana/transactions@6.2.0': + resolution: {integrity: sha512-ItOgWG5F34lp44C9N+njj77pOFcnV82NZnt42l6VW03sfgMr3Pquj7uyYpmKeZ+hN1J+s8jDR1OxhQv7TMrEUg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: ^5.0.0 @@ -2381,69 +2400,69 @@ packages: '@swc/helpers@0.5.18': resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==} - '@tailwindcss/node@4.2.2': - resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} + '@tailwindcss/node@4.2.1': + resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} - '@tailwindcss/oxide-android-arm64@4.2.2': - resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} + '@tailwindcss/oxide-android-arm64@4.2.1': + resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.2.2': - resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} + '@tailwindcss/oxide-darwin-arm64@4.2.1': + resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.2.2': - resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} + '@tailwindcss/oxide-darwin-x64@4.2.1': + resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.2.2': - resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} + '@tailwindcss/oxide-freebsd-x64@4.2.1': + resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': - resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': - resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.2.2': - resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.2.2': - resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.2.2': - resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} + '@tailwindcss/oxide-linux-x64-musl@4.2.1': + resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.2.2': - resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} + '@tailwindcss/oxide-wasm32-wasi@4.2.1': + resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -2454,56 +2473,29 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': - resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.2.2': - resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.2.2': - resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} + '@tailwindcss/oxide@4.2.1': + resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} engines: {node: '>= 20'} - '@tailwindcss/vite@4.2.2': - resolution: {integrity: sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==} + '@tailwindcss/vite@4.2.1': + resolution: {integrity: sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==} peerDependencies: - vite: ^5.2.0 || ^6 || ^7 || ^8 - - '@turbo/darwin-64@2.8.20': - resolution: {integrity: sha512-FQ9EX1xMU5nbwjxXxM3yU88AQQ6Sqc6S44exPRroMcx9XZHqqppl5ymJF0Ig/z3nvQNwDmz1Gsnvxubo+nXWjQ==} - cpu: [x64] - os: [darwin] - - '@turbo/darwin-arm64@2.8.20': - resolution: {integrity: sha512-Gpyh9ATFGThD6/s9L95YWY54cizg/VRWl2B67h0yofG8BpHf67DFAh9nuJVKG7bY0+SBJDAo5cMur+wOl9YOYw==} - cpu: [arm64] - os: [darwin] - - '@turbo/linux-64@2.8.20': - resolution: {integrity: sha512-p2QxWUYyYUgUFG0b0kR+pPi8t7c9uaVlRtjTTI1AbCvVqkpjUfCcReBn6DgG/Hu8xrWdKLuyQFaLYFzQskZbcA==} - cpu: [x64] - os: [linux] - - '@turbo/linux-arm64@2.8.20': - resolution: {integrity: sha512-Gn5yjlZGLRZWarLWqdQzv0wMqyBNIdq1QLi48F1oY5Lo9kiohuf7BPQWtWxeNVS2NgJ1+nb/DzK1JduYC4AWOA==} - cpu: [arm64] - os: [linux] + vite: ^5.2.0 || ^6 || ^7 - '@turbo/windows-64@2.8.20': - resolution: {integrity: sha512-vyaDpYk/8T6Qz5V/X+ihKvKFEZFUoC0oxYpC1sZanK6gaESJlmV3cMRT3Qhcg4D2VxvtC2Jjs9IRkrZGL+exLw==} - cpu: [x64] - os: [win32] - - '@turbo/windows-arm64@2.8.20': - resolution: {integrity: sha512-voicVULvUV5yaGXo0Iue13BcHGYW3u0VgqSbfQwBaHbpj1zLjYV4KIe+7fYIo6DO8FVUJzxFps3ODCQG/Wy2Qw==} - cpu: [arm64] - os: [win32] + '@toon-format/toon@2.1.0': + resolution: {integrity: sha512-JwWptdF5eOA0HaQxbKAzkpQtR4wSWTEfDlEy/y3/4okmOAX1qwnpLZMmtEWr+ncAhTTY1raCKH0kteHhSXnQqg==} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -2511,8 +2503,8 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -2523,6 +2515,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -2589,57 +2584,51 @@ packages: resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} engines: {node: '>= 20'} - '@vitest/expect@4.1.0': - resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} + '@vitest/expect@4.0.18': + resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} - '@vitest/mocker@4.1.0': - resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} + '@vitest/mocker@4.0.18': + resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + vite: ^6.0.0 || ^7.0.0-0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.1.0': - resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} + '@vitest/pretty-format@4.0.18': + resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} - '@vitest/runner@4.1.0': - resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} + '@vitest/runner@4.0.18': + resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} - '@vitest/snapshot@4.1.0': - resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} + '@vitest/snapshot@4.0.18': + resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} - '@vitest/spy@4.1.0': - resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} + '@vitest/spy@4.0.18': + resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} - '@vitest/utils@4.1.0': - resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} + '@vitest/utils@4.0.18': + resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} '@x402/core@2.6.0': resolution: {integrity: sha512-ISC/JeVss6xlKvor2rp18tJf9K5OQlIDDfZW1VZJQGDI2F4gy+HWxxkFfcQalCsPp4YUlwqh0YOkUxP+LTZWVg==} - '@x402/core@2.7.0': - resolution: {integrity: sha512-2l1QaRO50qtWpnTJed45HtGRAUp3z2Mep1caNCuaNBzeiE8OfWfuXmip+ujgp3vWZHxRqag221aRSnKbA6gDaQ==} - '@x402/evm@2.6.0': resolution: {integrity: sha512-wPkNHf483gie1Up2sJSvERnW+VIEvMoT1KRXr09wSoSWgelglsJm+ug3gPPXKnT3C6AcmNAKZ12rBnu9Paff7g==} '@x402/extensions@2.6.0': resolution: {integrity: sha512-aLY9xAOOiRLKDN9HT2r9TYUXbD+IsoBces9qPZNVJGO2TBi2rfmbIBc3pcKCtWKn3iTvG2QFr3gpOFdpJRzqww==} - '@x402/extensions@2.7.0': - resolution: {integrity: sha512-aNi3sVhTiGqeNJgIzYAqM51zum9wu9P2wBTBYFbv1VFdiO+/u4qKNJMNiDdpC4PYtyLhDpZFx0Fc1j7wExmoUw==} - - '@x402/fetch@2.7.0': - resolution: {integrity: sha512-hkJvoVtYjybkhLk1K2kRB0aVVWaxz43qYBSGZo0wgnUuIUXzmArGohhM/VfG8kVqcl+NDIuCD8tFiKSONr9X6A==} + '@x402/fetch@2.6.0': + resolution: {integrity: sha512-OnHXw/mv76ig4UBJEgfQIWHSWcrgIOT2i8RxEuGl12QtaYwSgBcgDub2GdllL/iIB9OneM1m0UWlrPh23JdVjQ==} - '@x402/hono@2.7.0': - resolution: {integrity: sha512-6TKFF0pq7iMjJ+U3l7HeeVxEzmQuyTO2QUl6iszsTgMP9w4TEgXdMScJ+YQ4v6WrxFS1icwImbwn1K7MX/RLlg==} + '@x402/hono@2.6.0': + resolution: {integrity: sha512-Wt+7Ik6gnVNEY57O+A8/hueL9j+DvbNtHvvqKkTvpq6gadvU2ZtOn++0sWtDIFKRaTq3MVix/rWKlNCRoG0hDQ==} peerDependencies: - '@x402/paywall': ^2.7.0 + '@x402/paywall': 2.6.0 hono: ^4.0.0 peerDependenciesMeta: '@x402/paywall': @@ -2694,6 +2683,14 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -3007,9 +3004,6 @@ packages: engines: {node: '>=18'} hasBin: true - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-es@1.2.2: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} @@ -3080,6 +3074,10 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -3130,8 +3128,8 @@ packages: resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} engines: {node: '>=18'} - devalue@5.6.3: - resolution: {integrity: sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==} + devalue@5.6.4: + resolution: {integrity: sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==} devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -3188,8 +3186,8 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - enhanced-resolve@5.20.1: - resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + enhanced-resolve@5.20.0: + resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -3222,9 +3220,6 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - es-module-lexer@2.0.0: - resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} - es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -3426,9 +3421,10 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - git-raw-commits@5.0.1: - resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} - engines: {node: '>=18'} + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. hasBin: true github-slugger@2.0.0: @@ -3445,8 +3441,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - h3@1.15.6: - resolution: {integrity: sha512-oi15ESLW5LRthZ+qPCi5GNasY/gvynSKUQxgiovrY63bPAtG59wtM+LSrlcwvOHAXzGrXVLnI97brbkdPF9WoQ==} + h3@1.15.9: + resolution: {integrity: sha512-H7UPnyIupUOYUQu7f2x7ABVeMyF/IbJjqn20WSXpMdnQB260luADUkSgJU7QTWLutq8h3tUayMQ1DdbSYX5LkA==} has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -3504,8 +3500,8 @@ packages: resolution: {integrity: sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg==} engines: {node: '>=16.9.0'} - hono@4.12.8: - resolution: {integrity: sha512-VJCEvtrezO1IAR+kqEYnxUOoStaQPGrCmX3j4wDTNOcD1uRPFpGlwQUIW8niPuvHXaTUxeOUl5MMDGrl+tmO9A==} + hono@4.12.6: + resolution: {integrity: sha512-KljEp+MeEEEIOT75qBo1UjqqB29fRMtlDEwCxcexOzdkUq6LR/vRvHk5pdROcxyOYyW1niq7Gb5pFVGy5R1eBw==} engines: {node: '>=16.9.0'} html-escaper@3.0.3: @@ -3546,6 +3542,10 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + incur@0.3.4: + resolution: {integrity: sha512-pXqZCRZXBBFhPPte0//I6SLtgCVuY9Subd8/kpyKhMrQjSCViuc72f5bJ4UeV4QO5CzG5f6GQYocPAljjhW3Lg==} + hasBin: true + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -3630,10 +3630,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isolated-vm@6.1.2: - resolution: {integrity: sha512-GGfsHqtlZiiurZaxB/3kY7LLAXR3sgzDul0fom4cSyBjx6ZbjpTrFWiH3z/nUfLJGJ8PIq9LQmQFiAxu24+I7A==} - engines: {node: '>=22.0.0'} - isomorphic-ws@4.0.1: resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: @@ -3657,9 +3653,6 @@ packages: resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} engines: {node: '>= 0.6.0'} - jose@5.10.0: - resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} - jose@6.1.3: resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} @@ -3695,6 +3688,10 @@ packages: json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + jsonrepair@3.13.2: resolution: {integrity: sha512-Leuly0nbM4R+S5SVJk3VHfw1oxnlEK9KygdZvfUtEtTawNDyzB4qa1xWTmFt1aeoA7sXZkVTRuIixJ8bAvqVUg==} hasBin: true @@ -3706,81 +3703,85 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + light-bolt11-decoder@3.2.0: resolution: {integrity: sha512-3QEofgiBOP4Ehs9BI+RkZdXZNtSys0nsJ6fyGeSiAGCBsMwHGUDS/JQlY/sTnWs91A2Nh0S9XXfA8Sy9g6QpuQ==} - lightningcss-android-arm64@1.32.0: - resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + lightningcss-android-arm64@1.31.1: + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] - lightningcss-darwin-arm64@1.32.0: - resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + lightningcss-darwin-arm64@1.31.1: + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.32.0: - resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + lightningcss-darwin-x64@1.31.1: + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.32.0: - resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + lightningcss-freebsd-x64@1.31.1: + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + lightningcss-linux-arm-gnueabihf@1.31.1: + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.32.0: - resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + lightningcss-linux-arm64-gnu@1.31.1: + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] - lightningcss-linux-arm64-musl@1.32.0: - resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + lightningcss-linux-arm64-musl@1.31.1: + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] - lightningcss-linux-x64-gnu@1.32.0: - resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + lightningcss-linux-x64-gnu@1.31.1: + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] - lightningcss-linux-x64-musl@1.32.0: - resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + lightningcss-linux-x64-musl@1.31.1: + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] - lightningcss-win32-arm64-msvc@1.32.0: - resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + lightningcss-win32-arm64-msvc@1.31.1: + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.32.0: - resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + lightningcss-win32-x64-msvc@1.31.1: + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.32.0: - resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + lightningcss@1.31.1: + resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} engines: {node: '>= 12.0.0'} lilconfig@3.1.3: @@ -3822,6 +3823,10 @@ packages: resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} + lru-cache@11.2.7: + resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + engines: {node: 20 || >=22} + luxon@3.7.2: resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} @@ -3902,6 +3907,10 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} @@ -4016,6 +4025,25 @@ packages: mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + mppx@0.4.7: + resolution: {integrity: sha512-urfpeTjm8SBmUaciqrDRPY0DgD2+3RTAITRenQrHf9fgDEakQ/ccc715seuecU57WUwZx0Tw5ia0M4IfOz1O/w==} + hasBin: true + peerDependencies: + '@modelcontextprotocol/sdk': '>=1.25.0' + elysia: '>=1' + express: '>=5' + hono: '>=4' + viem: '>=2.46.2' + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + elysia: + optional: true + express: + optional: true + hono: + optional: true + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -4030,8 +4058,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - n8n-workflow@2.13.0: - resolution: {integrity: sha512-fYLyYDlenGlzNvC+i+xsiFSna7gZVbhhTTb215ygL7JVguJhWy7JqW8szhvxkJMqujP0ejlbJYzPfRVxNyllkQ==} + n8n-workflow@2.11.1: + resolution: {integrity: sha512-pln5b5Uu8f0ecaIdRt7RBxulatJz5DGZ6azqzGmekavYtMI3XIqzF60cozO0bXjRJ8mGUEZXcvlttMnIp5dxxg==} nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} @@ -4129,8 +4157,11 @@ packages: oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} - oniguruma-to-es@4.3.4: - resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} + oniguruma-to-es@4.3.5: + resolution: {integrity: sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} ox@0.12.4: resolution: {integrity: sha512-+P+C7QzuwPV8lu79dOwjBKfB2CbnbEXe/hfyyrff1drrO1nOOj3Hc87svHfcW1yneRr3WXaKr6nz11nq+/DF9Q==} @@ -4255,6 +4286,10 @@ packages: yaml: optional: true + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.5.8: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} @@ -4502,6 +4537,10 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -4509,8 +4548,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.0.0: - resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} stream-chain@2.2.5: resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} @@ -4559,8 +4598,8 @@ packages: engines: {node: '>=16'} hasBin: true - tailwindcss@4.2.2: - resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} + tailwindcss@4.2.1: + resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} @@ -4597,8 +4636,8 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} title-case@3.0.3: @@ -4608,6 +4647,9 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + tokenx@1.3.0: + resolution: {integrity: sha512-NLdXTEZkKiO0gZuLtMoZKjCXTREXeZZt8nnnNeyoXtNZAfG/GKGSbQtLU5STspc0rMSwcA+UJfWZkbNU01iKmQ==} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -4664,8 +4706,38 @@ packages: typescript: optional: true - turbo@2.8.20: - resolution: {integrity: sha512-Rb4qk5YT8RUwwdXtkLpkVhNEe/lor6+WV7S5tTlLpxSz6MjV5Qi8jGNn4gS6NAvrYGA/rNrE6YUQM85sCZUDbQ==} + turbo-darwin-64@2.8.15: + resolution: {integrity: sha512-EElCh+Ltxex9lXYrouV3hHjKP3HFP31G91KMghpNHR/V99CkFudRcHcnWaorPbzAZizH1m8o2JkLL8rptgb8WQ==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.8.15: + resolution: {integrity: sha512-ORmvtqHiHwvNynSWvLIleyU8dKtwQ4ILk39VsEwfKSEzSHWYWYxZhBmD9GAGRPlNl7l7S1irrziBlDEGVpq+vQ==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.8.15: + resolution: {integrity: sha512-Bk1E61a+PCWUTfhqfXFlhEJMLp6nak0J0Qt14IZX1og1zyaiBLkM6M1GQFbPpiWfbUcdLwRaYQhO0ySB07AJ8w==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.8.15: + resolution: {integrity: sha512-3BX0Vk+XkP0uiZc8pkjQGNsAWjk5ojC53bQEMp6iuhSdWpEScEFmcT6p7DL7bcJmhP2mZ1HlAu0A48wrTGCtvg==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.8.15: + resolution: {integrity: sha512-m14ogunMF+grHZ1jzxSCO6q0gEfF1tmr+0LU+j1QNd/M1X33tfKnQqmpkeUR/REsGjfUlkQlh6PAzqlT3cA3Pg==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.8.15: + resolution: {integrity: sha512-HWh6dnzhl7nu5gRwXeqP61xbyDBNmQ4UCeWNa+si4/6RAtHlKEcZTNs7jf4U+oqBnbtv4uxbKZZPf/kN0EK4+A==} + cpu: [arm64] + os: [win32] + + turbo@2.8.15: + resolution: {integrity: sha512-ERZf7pKOR155NKs/PZt1+83NrSEJfUL7+p9/TGZg/8xzDVMntXEFQlX4CsNJQTyu4h3j+dZYiQWOOlv5pssuHQ==} hasBin: true tweetnacl@1.0.3: @@ -4707,8 +4779,8 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici-types@7.24.4: - resolution: {integrity: sha512-cRaY9PagdEZoRmcwzk3tUV3SVGrVQkR6bcSilav/A0vXsfpW4Lvd0BvgRMwTEDTLLGN+QdyBTG+nnvTgJhdt6w==} + undici-types@7.22.0: + resolution: {integrity: sha512-RKZvifiL60xdsIuC80UY0dq8Z7DbJUV8/l2hOVbyZAxBzEeQU4Z58+4ZzJ6WN2Lidi9KzT5EbiGX+PI/UGYuRw==} unicode-emoji-modifier-base@1.0.0: resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} @@ -4823,10 +4895,6 @@ packages: util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -4867,14 +4935,6 @@ packages: typescript: optional: true - viem@2.47.4: - resolution: {integrity: sha512-h0Wp/SYmJO/HB4B/em1OZ3W1LaKrmr7jzaN7talSlZpo0LCn0V6rZ5g923j6sf4VUSrqp/gUuWuHFc7UcoIp8A==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - vite@6.4.1: resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -4963,21 +5023,20 @@ packages: vite: optional: true - vitest@4.1.0: - resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} + vitest@4.0.18: + resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.0 - '@vitest/browser-preview': 4.1.0 - '@vitest/browser-webdriverio': 4.1.0 - '@vitest/ui': 4.1.0 + '@vitest/browser-playwright': 4.0.18 + '@vitest/browser-preview': 4.0.18 + '@vitest/browser-webdriverio': 4.0.18 + '@vitest/ui': 4.0.18 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -5185,6 +5244,11 @@ snapshots: '@andrewbranch/untar.js@1.0.3': {} + '@apidevtools/json-schema-ref-parser@14.2.1(@types/json-schema@7.0.15)': + dependencies: + '@types/json-schema': 7.0.15 + js-yaml: 4.1.1 + '@arethetypeswrong/cli@0.18.2': dependencies: '@arethetypeswrong/core': 0.18.2 @@ -5262,7 +5326,7 @@ snapshots: '@babel/helper-validator-identifier@7.28.5': {} - '@babel/parser@7.29.0': + '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 @@ -5339,34 +5403,32 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@commitlint/cli@20.5.0(@types/node@25.3.5)(conventional-commits-parser@6.3.0)(typescript@5.9.3)': + '@commitlint/cli@20.4.3(@types/node@25.3.5)(typescript@5.9.3)': dependencies: - '@commitlint/format': 20.5.0 - '@commitlint/lint': 20.5.0 - '@commitlint/load': 20.5.0(@types/node@25.3.5)(typescript@5.9.3) - '@commitlint/read': 20.5.0(conventional-commits-parser@6.3.0) - '@commitlint/types': 20.5.0 - tinyexec: 1.0.4 + '@commitlint/format': 20.4.3 + '@commitlint/lint': 20.4.3 + '@commitlint/load': 20.4.3(@types/node@25.3.5)(typescript@5.9.3) + '@commitlint/read': 20.4.3 + '@commitlint/types': 20.4.3 + tinyexec: 1.0.2 yargs: 17.7.2 transitivePeerDependencies: - '@types/node' - - conventional-commits-filter - - conventional-commits-parser - typescript - '@commitlint/config-conventional@20.5.0': + '@commitlint/config-conventional@20.4.3': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 20.4.3 conventional-changelog-conventionalcommits: 9.3.0 - '@commitlint/config-validator@20.5.0': + '@commitlint/config-validator@20.4.3': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 20.4.3 ajv: 8.18.0 - '@commitlint/ensure@20.5.0': + '@commitlint/ensure@20.4.3': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 20.4.3 lodash.camelcase: 4.3.0 lodash.kebabcase: 4.1.1 lodash.snakecase: 4.1.1 @@ -5375,29 +5437,29 @@ snapshots: '@commitlint/execute-rule@20.0.0': {} - '@commitlint/format@20.5.0': + '@commitlint/format@20.4.3': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 20.4.3 picocolors: 1.1.1 - '@commitlint/is-ignored@20.5.0': + '@commitlint/is-ignored@20.4.3': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 20.4.3 semver: 7.7.4 - '@commitlint/lint@20.5.0': + '@commitlint/lint@20.4.3': dependencies: - '@commitlint/is-ignored': 20.5.0 - '@commitlint/parse': 20.5.0 - '@commitlint/rules': 20.5.0 - '@commitlint/types': 20.5.0 + '@commitlint/is-ignored': 20.4.3 + '@commitlint/parse': 20.4.3 + '@commitlint/rules': 20.4.3 + '@commitlint/types': 20.4.3 - '@commitlint/load@20.5.0(@types/node@25.3.5)(typescript@5.9.3)': + '@commitlint/load@20.4.3(@types/node@25.3.5)(typescript@5.9.3)': dependencies: - '@commitlint/config-validator': 20.5.0 + '@commitlint/config-validator': 20.4.3 '@commitlint/execute-rule': 20.0.0 - '@commitlint/resolve-extends': 20.5.0 - '@commitlint/types': 20.5.0 + '@commitlint/resolve-extends': 20.4.3 + '@commitlint/types': 20.4.3 cosmiconfig: 9.0.1(typescript@5.9.3) cosmiconfig-typescript-loader: 6.2.0(@types/node@25.3.5)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) is-plain-obj: 4.1.0 @@ -5409,38 +5471,35 @@ snapshots: '@commitlint/message@20.4.3': {} - '@commitlint/parse@20.5.0': + '@commitlint/parse@20.4.3': dependencies: - '@commitlint/types': 20.5.0 + '@commitlint/types': 20.4.3 conventional-changelog-angular: 8.3.0 conventional-commits-parser: 6.3.0 - '@commitlint/read@20.5.0(conventional-commits-parser@6.3.0)': + '@commitlint/read@20.4.3': dependencies: '@commitlint/top-level': 20.4.3 - '@commitlint/types': 20.5.0 - git-raw-commits: 5.0.1(conventional-commits-parser@6.3.0) + '@commitlint/types': 20.4.3 + git-raw-commits: 4.0.0 minimist: 1.2.8 - tinyexec: 1.0.4 - transitivePeerDependencies: - - conventional-commits-filter - - conventional-commits-parser + tinyexec: 1.0.2 - '@commitlint/resolve-extends@20.5.0': + '@commitlint/resolve-extends@20.4.3': dependencies: - '@commitlint/config-validator': 20.5.0 - '@commitlint/types': 20.5.0 + '@commitlint/config-validator': 20.4.3 + '@commitlint/types': 20.4.3 global-directory: 4.0.1 import-meta-resolve: 4.2.0 lodash.mergewith: 4.6.2 resolve-from: 5.0.0 - '@commitlint/rules@20.5.0': + '@commitlint/rules@20.4.3': dependencies: - '@commitlint/ensure': 20.5.0 + '@commitlint/ensure': 20.4.3 '@commitlint/message': 20.4.3 '@commitlint/to-lines': 20.0.0 - '@commitlint/types': 20.5.0 + '@commitlint/types': 20.4.3 '@commitlint/to-lines@20.0.0': {} @@ -5448,20 +5507,12 @@ snapshots: dependencies: escalade: 3.2.0 - '@commitlint/types@20.5.0': + '@commitlint/types@20.4.3': dependencies: conventional-commits-parser: 6.3.0 picocolors: 1.1.1 - '@conventional-changelog/git-client@2.6.0(conventional-commits-parser@6.3.0)': - dependencies: - '@simple-libs/child-process-utils': 1.0.2 - '@simple-libs/stream-utils': 1.2.0 - semver: 7.7.4 - optionalDependencies: - conventional-commits-parser: 6.3.0 - - '@emnapi/runtime@1.8.1': + '@emnapi/runtime@1.9.1': dependencies: tslib: 2.8.1 optional: true @@ -5719,9 +5770,11 @@ snapshots: dependencies: hono: 4.12.5 - '@hono/node-server@1.19.11(hono@4.12.8)': + '@hono/node-server@1.19.11(hono@4.12.6)': dependencies: - hono: 4.12.8 + hono: 4.12.6 + + '@humanwhocodes/momoa@2.0.4': {} '@img/colour@1.1.0': optional: true @@ -5808,7 +5861,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.8.1 + '@emnapi/runtime': 1.9.1 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -5843,6 +5896,29 @@ snapshots: dependencies: '@braidai/lang': 1.1.2 + '@modelcontextprotocol/sdk@1.27.1(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.11(hono@4.12.5) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.3.0(express@5.2.1) + hono: 4.12.5 + jose: 6.2.0 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.1(zod@3.25.76) + transitivePeerDependencies: + - supports-color + optional: true + '@modelcontextprotocol/sdk@1.27.1(zod@4.3.6)': dependencies: '@hono/node-server': 1.19.11(hono@4.12.5) @@ -5869,18 +5945,6 @@ snapshots: dependencies: callsites: 3.1.0 - '@n8n/expression-runtime@0.5.0': - dependencies: - '@n8n/tournament': 1.0.6 - isolated-vm: 6.1.2 - js-base64: 3.7.2 - jssha: 3.3.1 - lodash: 4.17.23 - luxon: 3.7.2 - md5: 2.3.0 - title-case: 3.0.3 - transliteration: 2.3.5 - '@n8n/tournament@1.0.6': dependencies: '@n8n_io/riot-tmpl': 4.0.1 @@ -5924,6 +5988,36 @@ snapshots: '@publint/pack@0.1.4': {} + '@readme/better-ajv-errors@2.4.0(ajv@8.18.0)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.28.6 + '@humanwhocodes/momoa': 2.0.4 + ajv: 8.18.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + picocolors: 1.1.1 + + '@readme/openapi-parser@6.0.0(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) + '@readme/better-ajv-errors': 2.4.0(ajv@8.18.0) + '@readme/openapi-schemas': 3.1.0 + '@types/json-schema': 7.0.15 + ajv: 8.18.0 + ajv-draft-04: 1.0.0(ajv@8.18.0) + openapi-types: 12.1.3 + + '@readme/openapi-schemas@3.1.0': {} + + '@remix-run/fetch-proxy@0.7.1': + dependencies: + '@remix-run/headers': 0.19.0 + + '@remix-run/headers@0.19.0': {} + + '@remix-run/node-fetch-server@0.13.0': {} + '@rollup/pluginutils@5.3.0(rollup@4.59.0)': dependencies: '@types/estree': 1.0.8 @@ -6090,7 +6184,7 @@ snapshots: '@scure/bip32@1.7.0': dependencies: - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 @@ -6121,7 +6215,7 @@ snapshots: dependencies: '@shikijs/types': 3.23.0 '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.4 + oniguruma-to-es: 4.3.5 '@shikijs/engine-oniguruma@3.23.0': dependencies: @@ -6143,34 +6237,30 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} - '@simple-libs/child-process-utils@1.0.2': - dependencies: - '@simple-libs/stream-utils': 1.2.0 - '@simple-libs/stream-utils@1.2.0': {} '@sindresorhus/is@4.6.0': {} - '@solana-program/compute-budget@0.11.0(@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))': + '@solana-program/compute-budget@0.11.0(@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/kit': 6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/kit': 6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) '@solana-program/system@0.10.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))': dependencies: '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana-program/token-2022@0.6.1(@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))(@solana/sysvars@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))': + '@solana-program/token-2022@0.6.1(@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))(@solana/sysvars@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))': dependencies: - '@solana/kit': 6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/sysvars': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/kit': 6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/sysvars': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@solana-program/token@0.9.0(@solana/kit@5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))': dependencies: '@solana/kit': 5.5.1(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana-program/token@0.9.0(@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))': + '@solana-program/token@0.9.0(@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))': dependencies: - '@solana/kit': 6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/kit': 6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) '@solana/accounts@5.5.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: @@ -6185,14 +6275,14 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/accounts@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/accounts@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec': 6.5.0(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec': 6.2.0(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6210,13 +6300,13 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/addresses@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/addresses@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/assertions': 6.5.0(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/nominal-types': 6.5.0(typescript@5.9.3) + '@solana/assertions': 6.2.0(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/nominal-types': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6228,9 +6318,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/assertions@6.5.0(typescript@5.9.3)': + '@solana/assertions@6.2.0(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -6249,9 +6339,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/codecs-core@6.5.0(typescript@5.9.3)': + '@solana/codecs-core@6.2.0(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -6263,11 +6353,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/codecs-data-structures@6.5.0(typescript@5.9.3)': + '@solana/codecs-data-structures@6.2.0(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -6284,10 +6374,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/codecs-numbers@6.5.0(typescript@5.9.3)': + '@solana/codecs-numbers@6.2.0(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -6300,11 +6390,11 @@ snapshots: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.9.3 - '@solana/codecs-strings@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/codecs-strings@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.9.3 @@ -6321,13 +6411,13 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/codecs@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/codecs@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-data-structures': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/options': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/options': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6346,7 +6436,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/errors@6.5.0(typescript@5.9.3)': + '@solana/errors@6.2.0(typescript@5.9.3)': dependencies: chalk: 5.6.2 commander: 14.0.3 @@ -6357,7 +6447,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/fast-stable-stringify@6.5.0(typescript@5.9.3)': + '@solana/fast-stable-stringify@6.2.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -6365,7 +6455,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/functional@6.5.0(typescript@5.9.3)': + '@solana/functional@6.2.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -6382,14 +6472,14 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/instruction-plans@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/instruction-plans@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/instructions': 6.5.0(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/promises': 6.5.0(typescript@5.9.3) - '@solana/transaction-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/instructions': 6.2.0(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/promises': 6.2.0(typescript@5.9.3) + '@solana/transaction-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transactions': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6402,10 +6492,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/instructions@6.5.0(typescript@5.9.3)': + '@solana/instructions@6.2.0(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -6421,13 +6511,13 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/keys@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/keys@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/assertions': 6.5.0(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/nominal-types': 6.5.0(typescript@5.9.3) + '@solana/assertions': 6.2.0(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/nominal-types': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6464,32 +6554,32 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': - dependencies: - '@solana/accounts': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/functional': 6.5.0(typescript@5.9.3) - '@solana/instruction-plans': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/instructions': 6.5.0(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/offchain-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/plugin-core': 6.5.0(typescript@5.9.3) - '@solana/plugin-interfaces': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/program-client-core': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/programs': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-api': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-parsed-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-subscriptions': 6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/signers': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/sysvars': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-confirmation': 6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/transaction-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/accounts': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/functional': 6.2.0(typescript@5.9.3) + '@solana/instruction-plans': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/instructions': 6.2.0(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/offchain-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/plugin-core': 6.2.0(typescript@5.9.3) + '@solana/plugin-interfaces': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/program-client-core': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/programs': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-api': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-parsed-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions': 6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/signers': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/sysvars': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transaction-confirmation': 6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/transaction-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transactions': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6501,7 +6591,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/nominal-types@6.5.0(typescript@5.9.3)': + '@solana/nominal-types@6.2.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -6520,16 +6610,16 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/offchain-messages@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/offchain-messages@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-data-structures': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/nominal-types': 6.5.0(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/nominal-types': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6547,13 +6637,13 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/options@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/options@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-data-structures': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6563,35 +6653,35 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/plugin-core@6.5.0(typescript@5.9.3)': + '@solana/plugin-core@6.2.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 - '@solana/plugin-interfaces@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/plugin-interfaces@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/instruction-plans': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-spec': 6.5.0(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 6.5.0(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/signers': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/instruction-plans': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-spec': 6.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 6.2.0(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/signers': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/program-client-core@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/program-client-core@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/accounts': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/instruction-plans': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/instructions': 6.5.0(typescript@5.9.3) - '@solana/plugin-interfaces': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-api': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/signers': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/accounts': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/instruction-plans': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/instructions': 6.2.0(typescript@5.9.3) + '@solana/plugin-interfaces': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-api': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/signers': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6606,10 +6696,10 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/programs@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/programs@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6619,7 +6709,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/promises@6.5.0(typescript@5.9.3)': + '@solana/promises@6.2.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -6641,19 +6731,19 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-api@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/rpc-api@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-parsed-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec': 6.5.0(typescript@5.9.3) - '@solana/rpc-transformers': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-parsed-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec': 6.2.0(typescript@5.9.3) + '@solana/rpc-transformers': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transaction-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transactions': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6663,7 +6753,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/rpc-parsed-types@6.5.0(typescript@5.9.3)': + '@solana/rpc-parsed-types@6.2.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -6671,7 +6761,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/rpc-spec-types@6.5.0(typescript@5.9.3)': + '@solana/rpc-spec-types@6.2.0(typescript@5.9.3)': optionalDependencies: typescript: 5.9.3 @@ -6682,10 +6772,10 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/rpc-spec@6.5.0(typescript@5.9.3)': + '@solana/rpc-spec@6.2.0(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec-types': 6.5.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -6703,15 +6793,15 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-api@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/rpc-subscriptions-api@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 6.5.0(typescript@5.9.3) - '@solana/rpc-transformers': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 6.2.0(typescript@5.9.3) + '@solana/rpc-transformers': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transaction-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transactions': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6730,12 +6820,12 @@ snapshots: - bufferutil - utf-8-validate - '@solana/rpc-subscriptions-channel-websocket@6.5.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/rpc-subscriptions-channel-websocket@6.2.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/functional': 6.5.0(typescript@5.9.3) - '@solana/rpc-subscriptions-spec': 6.5.0(typescript@5.9.3) - '@solana/subscribable': 6.5.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/functional': 6.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-spec': 6.2.0(typescript@5.9.3) + '@solana/subscribable': 6.2.0(typescript@5.9.3) ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.3 @@ -6752,12 +6842,12 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/rpc-subscriptions-spec@6.5.0(typescript@5.9.3)': + '@solana/rpc-subscriptions-spec@6.2.0(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/promises': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec-types': 6.5.0(typescript@5.9.3) - '@solana/subscribable': 6.5.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/promises': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 6.2.0(typescript@5.9.3) + '@solana/subscribable': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -6781,19 +6871,19 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@solana/rpc-subscriptions@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/rpc-subscriptions@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/fast-stable-stringify': 6.5.0(typescript@5.9.3) - '@solana/functional': 6.5.0(typescript@5.9.3) - '@solana/promises': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-subscriptions-api': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions-channel-websocket': 6.5.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-spec': 6.5.0(typescript@5.9.3) - '@solana/rpc-transformers': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/subscribable': 6.5.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/fast-stable-stringify': 6.2.0(typescript@5.9.3) + '@solana/functional': 6.2.0(typescript@5.9.3) + '@solana/promises': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-subscriptions-api': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-subscriptions-channel-websocket': 6.2.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-subscriptions-spec': 6.2.0(typescript@5.9.3) + '@solana/rpc-transformers': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/subscribable': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6813,13 +6903,13 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-transformers@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/rpc-transformers@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/functional': 6.5.0(typescript@5.9.3) - '@solana/nominal-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/functional': 6.2.0(typescript@5.9.3) + '@solana/nominal-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6830,16 +6920,16 @@ snapshots: '@solana/errors': 5.5.1(typescript@5.9.3) '@solana/rpc-spec': 5.5.1(typescript@5.9.3) '@solana/rpc-spec-types': 5.5.1(typescript@5.9.3) - undici-types: 7.24.4 + undici-types: 7.22.0 optionalDependencies: typescript: 5.9.3 - '@solana/rpc-transport-http@6.5.0(typescript@5.9.3)': + '@solana/rpc-transport-http@6.2.0(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec-types': 6.5.0(typescript@5.9.3) - undici-types: 7.24.4 + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 6.2.0(typescript@5.9.3) + undici-types: 7.22.0 optionalDependencies: typescript: 5.9.3 @@ -6856,14 +6946,14 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-types@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/rpc-types@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/nominal-types': 6.5.0(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/nominal-types': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6885,17 +6975,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/rpc@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/fast-stable-stringify': 6.5.0(typescript@5.9.3) - '@solana/functional': 6.5.0(typescript@5.9.3) - '@solana/rpc-api': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-spec': 6.5.0(typescript@5.9.3) - '@solana/rpc-spec-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-transformers': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-transport-http': 6.5.0(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/fast-stable-stringify': 6.2.0(typescript@5.9.3) + '@solana/functional': 6.2.0(typescript@5.9.3) + '@solana/rpc-api': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-spec': 6.2.0(typescript@5.9.3) + '@solana/rpc-spec-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-transformers': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-transport-http': 6.2.0(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6917,17 +7007,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/signers@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/signers@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/instructions': 6.5.0(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/nominal-types': 6.5.0(typescript@5.9.3) - '@solana/offchain-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/instructions': 6.2.0(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/nominal-types': 6.2.0(typescript@5.9.3) + '@solana/offchain-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transaction-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transactions': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6939,9 +7029,9 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@solana/subscribable@6.5.0(typescript@5.9.3)': + '@solana/subscribable@6.2.0(typescript@5.9.3)': dependencies: - '@solana/errors': 6.5.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 @@ -6956,14 +7046,14 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/sysvars@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/sysvars@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/accounts': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-data-structures': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/accounts': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -6988,18 +7078,18 @@ snapshots: - fastestsmallesttextencoderdecoder - utf-8-validate - '@solana/transaction-confirmation@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@solana/transaction-confirmation@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/promises': 6.5.0(typescript@5.9.3) - '@solana/rpc': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/rpc-subscriptions': 6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transactions': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/promises': 6.2.0(typescript@5.9.3) + '@solana/rpc': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/rpc-subscriptions': 6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transaction-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transactions': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -7023,17 +7113,17 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-messages@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + '@solana/transaction-messages@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-data-structures': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/functional': 6.5.0(typescript@5.9.3) - '@solana/instructions': 6.5.0(typescript@5.9.3) - '@solana/nominal-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/functional': 6.2.0(typescript@5.9.3) + '@solana/instructions': 6.2.0(typescript@5.9.3) + '@solana/nominal-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -7058,20 +7148,20 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transactions@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': - dependencies: - '@solana/addresses': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/codecs-core': 6.5.0(typescript@5.9.3) - '@solana/codecs-data-structures': 6.5.0(typescript@5.9.3) - '@solana/codecs-numbers': 6.5.0(typescript@5.9.3) - '@solana/codecs-strings': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/errors': 6.5.0(typescript@5.9.3) - '@solana/functional': 6.5.0(typescript@5.9.3) - '@solana/instructions': 6.5.0(typescript@5.9.3) - '@solana/keys': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/nominal-types': 6.5.0(typescript@5.9.3) - '@solana/rpc-types': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) - '@solana/transaction-messages': 6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transactions@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': + dependencies: + '@solana/addresses': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/codecs-core': 6.2.0(typescript@5.9.3) + '@solana/codecs-data-structures': 6.2.0(typescript@5.9.3) + '@solana/codecs-numbers': 6.2.0(typescript@5.9.3) + '@solana/codecs-strings': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/errors': 6.2.0(typescript@5.9.3) + '@solana/functional': 6.2.0(typescript@5.9.3) + '@solana/instructions': 6.2.0(typescript@5.9.3) + '@solana/keys': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/nominal-types': 6.2.0(typescript@5.9.3) + '@solana/rpc-types': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) + '@solana/transaction-messages': 6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -7126,91 +7216,75 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/node@4.2.2': + '@tailwindcss/node@4.2.1': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.20.1 + enhanced-resolve: 5.20.0 jiti: 2.6.1 - lightningcss: 1.32.0 + lightningcss: 1.31.1 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.2.2 + tailwindcss: 4.2.1 - '@tailwindcss/oxide-android-arm64@4.2.2': + '@tailwindcss/oxide-android-arm64@4.2.1': optional: true - '@tailwindcss/oxide-darwin-arm64@4.2.2': + '@tailwindcss/oxide-darwin-arm64@4.2.1': optional: true - '@tailwindcss/oxide-darwin-x64@4.2.2': + '@tailwindcss/oxide-darwin-x64@4.2.1': optional: true - '@tailwindcss/oxide-freebsd-x64@4.2.2': + '@tailwindcss/oxide-freebsd-x64@4.2.1': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.2.2': + '@tailwindcss/oxide-linux-x64-musl@4.2.1': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.2.2': + '@tailwindcss/oxide-wasm32-wasi@4.2.1': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': optional: true - '@tailwindcss/oxide@4.2.2': + '@tailwindcss/oxide@4.2.1': 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 + '@tailwindcss/oxide-android-arm64': 4.2.1 + '@tailwindcss/oxide-darwin-arm64': 4.2.1 + '@tailwindcss/oxide-darwin-x64': 4.2.1 + '@tailwindcss/oxide-freebsd-x64': 4.2.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.1 + '@tailwindcss/oxide-linux-x64-musl': 4.2.1 + '@tailwindcss/oxide-wasm32-wasi': 4.2.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - '@tailwindcss/vite@4.2.2(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))': + '@tailwindcss/vite@4.2.1(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2))': dependencies: - '@tailwindcss/node': 4.2.2 - '@tailwindcss/oxide': 4.2.2 - tailwindcss: 4.2.2 - vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2) - - '@turbo/darwin-64@2.8.20': - optional: true - - '@turbo/darwin-arm64@2.8.20': - optional: true + '@tailwindcss/node': 4.2.1 + '@tailwindcss/oxide': 4.2.1 + tailwindcss: 4.2.1 + vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) - '@turbo/linux-64@2.8.20': - optional: true - - '@turbo/linux-arm64@2.8.20': - optional: true - - '@turbo/windows-64@2.8.20': - optional: true - - '@turbo/windows-arm64@2.8.20': - optional: true + '@toon-format/toon@2.1.0': {} '@types/chai@5.2.3': dependencies: @@ -7221,7 +7295,7 @@ snapshots: dependencies: '@types/node': 12.20.55 - '@types/debug@4.1.12': + '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -7233,6 +7307,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/json-schema@7.0.15': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -7275,63 +7351,57 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vitest/expect@4.1.0': + '@vitest/expect@4.0.18': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 chai: 6.2.2 - tinyrainbow: 3.1.0 + tinyrainbow: 3.0.3 - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2))': dependencies: - '@vitest/spy': 4.1.0 + '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) - '@vitest/mocker@4.1.0(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2))': dependencies: - '@vitest/spy': 4.1.0 + '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) - '@vitest/pretty-format@4.1.0': + '@vitest/pretty-format@4.0.18': dependencies: - tinyrainbow: 3.1.0 + tinyrainbow: 3.0.3 - '@vitest/runner@4.1.0': + '@vitest/runner@4.0.18': dependencies: - '@vitest/utils': 4.1.0 + '@vitest/utils': 4.0.18 pathe: 2.0.3 - '@vitest/snapshot@4.1.0': + '@vitest/snapshot@4.0.18': dependencies: - '@vitest/pretty-format': 4.1.0 - '@vitest/utils': 4.1.0 + '@vitest/pretty-format': 4.0.18 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.0': {} + '@vitest/spy@4.0.18': {} - '@vitest/utils@4.1.0': + '@vitest/utils@4.0.18': dependencies: - '@vitest/pretty-format': 4.1.0 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + '@vitest/pretty-format': 4.0.18 + tinyrainbow: 3.0.3 '@x402/core@2.6.0': dependencies: zod: 3.25.76 - '@x402/core@2.7.0': - dependencies: - zod: 3.25.76 - '@x402/evm@2.6.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: '@x402/core': 2.6.0 @@ -7359,38 +7429,21 @@ snapshots: - typescript - utf-8-validate - '@x402/extensions@2.7.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10)': - dependencies: - '@noble/curves': 1.9.7 - '@scure/base': 1.2.6 - '@x402/core': 2.7.0 - ajv: 8.18.0 - jose: 5.10.0 - siwe: 2.3.2(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - tweetnacl: 1.0.3 - viem: 2.47.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - zod: 3.25.76 - transitivePeerDependencies: - - bufferutil - - ethers - - typescript - - utf-8-validate - - '@x402/fetch@2.7.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@x402/fetch@2.6.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@x402/core': 2.7.0 - viem: 2.47.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + '@x402/core': 2.6.0 + viem: 2.47.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - '@x402/hono@2.7.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(hono@4.12.8)(typescript@5.9.3)(utf-8-validate@5.0.10)': + '@x402/hono@2.6.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(hono@4.12.6)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: - '@x402/core': 2.7.0 - '@x402/extensions': 2.7.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10) - hono: 4.12.8 + '@x402/core': 2.6.0 + '@x402/extensions': 2.6.0(bufferutil@4.1.0)(ethers@6.16.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(typescript@5.9.3)(utf-8-validate@5.0.10) + hono: 4.12.6 zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -7398,12 +7451,12 @@ snapshots: - typescript - utf-8-validate - '@x402/svm@2.6.0(@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))(@solana/sysvars@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))': + '@x402/svm@2.6.0(@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))(@solana/sysvars@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3))': dependencies: - '@solana-program/compute-budget': 0.11.0(@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@solana-program/token': 0.9.0(@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@solana-program/token-2022': 0.6.1(@solana/kit@6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))(@solana/sysvars@6.5.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) - '@solana/kit': 6.5.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) + '@solana-program/compute-budget': 0.11.0(@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana-program/token': 0.9.0(@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@solana-program/token-2022': 0.6.1(@solana/kit@6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10))(@solana/sysvars@6.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)) + '@solana/kit': 6.2.0(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)(utf-8-validate@5.0.10) '@x402/core': 2.6.0 transitivePeerDependencies: - '@solana/sysvars' @@ -7418,6 +7471,11 @@ snapshots: typescript: 5.9.3 zod: 3.25.76 + abitype@1.2.3(typescript@5.9.3)(zod@4.3.6): + optionalDependencies: + typescript: 5.9.3 + zod: 4.3.6 + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -7439,6 +7497,10 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 4.3.6 + ajv-draft-04@1.0.0(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + ajv-formats@3.0.1(ajv@8.18.0): optionalDependencies: ajv: 8.18.0 @@ -7503,7 +7565,7 @@ snapshots: dependencies: tslib: 2.8.1 - astro@5.18.1(@types/node@25.3.5)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.32.0)(rollup@4.59.0)(typescript@5.9.3)(yaml@2.8.2): + astro@5.18.1(@types/node@25.3.5)(idb-keyval@6.2.2)(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.59.0)(typescript@5.9.3)(yaml@2.8.2): dependencies: '@astrojs/compiler': 2.13.1 '@astrojs/internal-helpers': 0.7.6 @@ -7523,12 +7585,12 @@ snapshots: cssesc: 3.0.0 debug: 4.4.3 deterministic-object-hash: 2.0.2 - devalue: 5.6.3 + devalue: 5.6.4 diff: 8.0.3 dlv: 1.1.3 dset: 3.1.4 es-module-lexer: 1.7.0 - esbuild: 0.27.3 + esbuild: 0.27.4 estree-walker: 3.0.3 flattie: 1.1.1 fontace: 0.4.1 @@ -7552,7 +7614,7 @@ snapshots: shiki: 3.23.0 smol-toml: 1.6.0 svgo: 4.0.1 - tinyexec: 1.0.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 tsconfck: 3.1.6(typescript@5.9.3) ultrahtml: 1.6.0 @@ -7560,8 +7622,8 @@ snapshots: unist-util-visit: 5.1.0 unstorage: 1.17.4(idb-keyval@6.2.2) vfile: 6.0.3 - vite: 6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2) - vitefu: 1.1.2(vite@6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) + vite: 6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) + vitefu: 1.1.2(vite@6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2)) xxhash-wasm: 1.1.0 yargs-parser: 21.1.1 yocto-spinner: 0.2.3 @@ -7835,8 +7897,6 @@ snapshots: '@simple-libs/stream-utils': 1.2.0 meow: 13.2.0 - convert-source-map@2.0.0: {} - cookie-es@1.2.2: {} cookie-signature@1.2.2: {} @@ -7904,6 +7964,8 @@ snapshots: dependencies: css-tree: 2.2.1 + dargs@8.1.0: {} + debug@4.4.3: dependencies: ms: 2.1.3 @@ -7942,7 +8004,7 @@ snapshots: dependencies: base-64: 1.0.0 - devalue@5.6.3: {} + devalue@5.6.4: {} devlop@1.1.0: dependencies: @@ -7994,7 +8056,7 @@ snapshots: encodeurl@2.0.0: {} - enhanced-resolve@5.20.1: + enhanced-resolve@5.20.0: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -8017,8 +8079,6 @@ snapshots: es-module-lexer@1.7.0: {} - es-module-lexer@2.0.0: {} - es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -8307,13 +8367,11 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - git-raw-commits@5.0.1(conventional-commits-parser@6.3.0): + git-raw-commits@4.0.0: dependencies: - '@conventional-changelog/git-client': 2.6.0(conventional-commits-parser@6.3.0) - meow: 13.2.0 - transitivePeerDependencies: - - conventional-commits-filter - - conventional-commits-parser + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 github-slugger@2.0.0: {} @@ -8325,7 +8383,7 @@ snapshots: graceful-fs@4.2.11: {} - h3@1.15.6: + h3@1.15.9: dependencies: cookie-es: 1.2.2 crossws: 0.3.5 @@ -8444,7 +8502,7 @@ snapshots: hono@4.12.5: {} - hono@4.12.8: {} + hono@4.12.6: {} html-escaper@3.0.3: {} @@ -8482,6 +8540,19 @@ snapshots: import-meta-resolve@4.2.0: {} + incur@0.3.4(openapi-types@12.1.3): + dependencies: + '@modelcontextprotocol/sdk': 1.27.1(zod@4.3.6) + '@readme/openapi-parser': 6.0.0(openapi-types@12.1.3) + '@toon-format/toon': 2.1.0 + tokenx: 1.3.0 + yaml: 2.8.2 + zod: 4.3.6 + transitivePeerDependencies: + - '@cfworker/json-schema' + - openapi-types + - supports-color + inherits@2.0.4: {} ini@4.1.1: {} @@ -8549,10 +8620,6 @@ snapshots: isexe@2.0.0: {} - isolated-vm@6.1.2: - dependencies: - node-gyp-build: 4.8.4 - isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -8583,8 +8650,6 @@ snapshots: jmespath@0.16.0: {} - jose@5.10.0: {} - jose@6.1.3: {} jose@6.2.0: {} @@ -8609,64 +8674,68 @@ snapshots: json-stringify-safe@5.0.1: {} + jsonpointer@5.0.1: {} + jsonrepair@3.13.2: {} jssha@3.3.1: {} kleur@3.0.3: {} + leven@3.1.0: {} + light-bolt11-decoder@3.2.0: dependencies: '@scure/base': 1.1.1 - lightningcss-android-arm64@1.32.0: + lightningcss-android-arm64@1.31.1: optional: true - lightningcss-darwin-arm64@1.32.0: + lightningcss-darwin-arm64@1.31.1: optional: true - lightningcss-darwin-x64@1.32.0: + lightningcss-darwin-x64@1.31.1: optional: true - lightningcss-freebsd-x64@1.32.0: + lightningcss-freebsd-x64@1.31.1: optional: true - lightningcss-linux-arm-gnueabihf@1.32.0: + lightningcss-linux-arm-gnueabihf@1.31.1: optional: true - lightningcss-linux-arm64-gnu@1.32.0: + lightningcss-linux-arm64-gnu@1.31.1: optional: true - lightningcss-linux-arm64-musl@1.32.0: + lightningcss-linux-arm64-musl@1.31.1: optional: true - lightningcss-linux-x64-gnu@1.32.0: + lightningcss-linux-x64-gnu@1.31.1: optional: true - lightningcss-linux-x64-musl@1.32.0: + lightningcss-linux-x64-musl@1.31.1: optional: true - lightningcss-win32-arm64-msvc@1.32.0: + lightningcss-win32-arm64-msvc@1.31.1: optional: true - lightningcss-win32-x64-msvc@1.32.0: + lightningcss-win32-x64-msvc@1.31.1: optional: true - lightningcss@1.32.0: + lightningcss@1.31.1: dependencies: detect-libc: 2.1.2 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 + lightningcss-android-arm64: 1.31.1 + lightningcss-darwin-arm64: 1.31.1 + lightningcss-darwin-x64: 1.31.1 + lightningcss-freebsd-x64: 1.31.1 + lightningcss-linux-arm-gnueabihf: 1.31.1 + lightningcss-linux-arm64-gnu: 1.31.1 + lightningcss-linux-arm64-musl: 1.31.1 + lightningcss-linux-x64-gnu: 1.31.1 + lightningcss-linux-x64-musl: 1.31.1 + lightningcss-win32-arm64-msvc: 1.31.1 + lightningcss-win32-x64-msvc: 1.31.1 lilconfig@3.1.3: {} @@ -8692,6 +8761,8 @@ snapshots: lru-cache@11.2.6: {} + lru-cache@11.2.7: {} + luxon@3.7.2: {} magic-string@0.30.21: @@ -8700,7 +8771,7 @@ snapshots: magicast@0.5.2: dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 source-map-js: 1.2.1 @@ -8853,6 +8924,8 @@ snapshots: media-typer@1.1.0: {} + meow@12.1.1: {} + meow@13.2.0: {} merge-descriptors@2.0.0: {} @@ -9028,7 +9101,7 @@ snapshots: micromark@4.0.2: dependencies: - '@types/debug': 4.1.12 + '@types/debug': 4.1.13 debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 @@ -9069,6 +9142,24 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.3 + mppx@0.4.7(@modelcontextprotocol/sdk@1.27.1(zod@3.25.76))(express@5.2.1)(hono@4.12.6)(openapi-types@12.1.3)(typescript@5.9.3)(viem@2.47.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76)): + dependencies: + '@remix-run/fetch-proxy': 0.7.1 + '@remix-run/node-fetch-server': 0.13.0 + incur: 0.3.4(openapi-types@12.1.3) + ox: 0.14.5(typescript@5.9.3)(zod@4.3.6) + viem: 2.47.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) + zod: 4.3.6 + optionalDependencies: + '@modelcontextprotocol/sdk': 1.27.1(zod@3.25.76) + express: 5.2.1 + hono: 4.12.6 + transitivePeerDependencies: + - '@cfworker/json-schema' + - openapi-types + - supports-color + - typescript + mri@1.2.0: {} mrmime@2.0.1: {} @@ -9081,10 +9172,9 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - n8n-workflow@2.13.0: + n8n-workflow@2.11.1: dependencies: '@n8n/errors': 0.6.0 - '@n8n/expression-runtime': 0.5.0 '@n8n/tournament': 1.0.6 ast-types: 0.16.1 callsites: 3.1.0 @@ -9100,7 +9190,6 @@ snapshots: recast: 0.22.0 title-case: 3.0.3 transliteration: 2.3.5 - uuid: 10.0.0 xml2js: 0.6.2 zod: 3.25.67 @@ -9127,7 +9216,8 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-gyp-build@4.8.4: {} + node-gyp-build@4.8.4: + optional: true node-mock-http@1.0.4: {} @@ -9191,12 +9281,14 @@ snapshots: oniguruma-parser@0.12.1: {} - oniguruma-to-es@4.3.4: + oniguruma-to-es@4.3.5: dependencies: oniguruma-parser: 0.12.1 regex: 6.1.0 regex-recursion: 6.0.2 + openapi-types@12.1.3: {} + ox@0.12.4(typescript@5.9.3)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -9227,7 +9319,7 @@ snapshots: transitivePeerDependencies: - zod - ox@0.14.5(typescript@5.9.3)(zod@3.25.76): + ox@0.14.5(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -9235,7 +9327,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.3)(zod@4.3.6) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 @@ -9323,6 +9415,12 @@ snapshots: postcss: 8.5.8 yaml: 2.8.2 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postcss@8.5.8: dependencies: nanoid: 3.3.11 @@ -9734,11 +9832,13 @@ snapshots: space-separated-tokens@2.0.2: {} + split2@4.2.0: {} + stackback@0.0.2: {} statuses@2.0.2: {} - std-env@4.0.0: {} + std-env@3.10.0: {} stream-chain@2.2.5: {} @@ -9800,9 +9900,9 @@ snapshots: css-what: 6.2.2 csso: 5.0.5 picocolors: 1.1.1 - sax: 1.5.0 + sax: 1.6.0 - tailwindcss@4.2.2: {} + tailwindcss@4.2.1: {} tapable@2.3.0: {} @@ -9831,7 +9931,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - tinyrainbow@3.1.0: {} + tinyrainbow@3.0.3: {} title-case@3.0.3: dependencies: @@ -9839,6 +9939,8 @@ snapshots: toidentifier@1.0.1: {} + tokenx@1.3.0: {} + tr46@0.0.3: {} transliteration@2.3.5: @@ -9889,14 +9991,32 @@ snapshots: - tsx - yaml - turbo@2.8.20: + turbo-darwin-64@2.8.15: + optional: true + + turbo-darwin-arm64@2.8.15: + optional: true + + turbo-linux-64@2.8.15: + optional: true + + turbo-linux-arm64@2.8.15: + optional: true + + turbo-windows-64@2.8.15: + optional: true + + turbo-windows-arm64@2.8.15: + optional: true + + turbo@2.8.15: optionalDependencies: - '@turbo/darwin-64': 2.8.20 - '@turbo/darwin-arm64': 2.8.20 - '@turbo/linux-64': 2.8.20 - '@turbo/linux-arm64': 2.8.20 - '@turbo/windows-64': 2.8.20 - '@turbo/windows-arm64': 2.8.20 + turbo-darwin-64: 2.8.15 + turbo-darwin-arm64: 2.8.15 + turbo-linux-64: 2.8.15 + turbo-linux-arm64: 2.8.15 + turbo-windows-64: 2.8.15 + turbo-windows-arm64: 2.8.15 tweetnacl@1.0.3: {} @@ -9924,7 +10044,7 @@ snapshots: undici-types@7.18.2: {} - undici-types@7.24.4: {} + undici-types@7.22.0: {} unicode-emoji-modifier-base@1.0.0: {} @@ -9993,8 +10113,8 @@ snapshots: anymatch: 3.1.3 chokidar: 5.0.0 destr: 2.0.5 - h3: 1.15.6 - lru-cache: 11.2.6 + h3: 1.15.9 + lru-cache: 11.2.7 node-fetch-native: 1.6.7 ofetch: 1.5.1 ufo: 1.6.3 @@ -10018,8 +10138,6 @@ snapshots: is-typed-array: 1.1.15 which-typed-array: 1.1.20 - uuid@10.0.0: {} - uuid@8.3.2: {} valid-url@1.0.9: {} @@ -10077,24 +10195,7 @@ snapshots: - utf-8-validate - zod - viem@2.47.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76): - dependencies: - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.9.3)(zod@3.25.76) - isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.14.5(typescript@5.9.3)(zod@3.25.76) - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - zod - - vite@6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2): + vite@6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -10106,98 +10207,118 @@ snapshots: '@types/node': 25.3.5 fsevents: 2.3.3 jiti: 2.6.1 - lightningcss: 1.32.0 + lightningcss: 1.31.1 yaml: 2.8.2 - vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2): + vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2): dependencies: - esbuild: 0.27.4 + esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - postcss: 8.5.8 - rollup: 4.59.0 + postcss: 8.5.6 + rollup: 4.57.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.15 fsevents: 2.3.3 jiti: 2.6.1 - lightningcss: 1.32.0 + lightningcss: 1.31.1 yaml: 2.8.2 - vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2): + vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2): dependencies: - esbuild: 0.27.4 + esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - postcss: 8.5.8 - rollup: 4.59.0 + postcss: 8.5.6 + rollup: 4.57.1 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.3.5 fsevents: 2.3.3 jiti: 2.6.1 - lightningcss: 1.32.0 + lightningcss: 1.31.1 yaml: 2.8.2 - vitefu@1.1.2(vite@6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)): + vitefu@1.1.2(vite@6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2)): optionalDependencies: - vite: 6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2) + vite: 6.4.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) - vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)): + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2): dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 - es-module-lexer: 2.0.0 + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 4.0.0 + std-env: 3.10.0 tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2) + tinyrainbow: 3.0.3 + vite: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 22.19.15 transitivePeerDependencies: + - jiti + - less + - lightningcss - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml - vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)): + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2): dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 - es-module-lexer: 2.0.0 + '@vitest/expect': 4.0.18 + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.18 + '@vitest/runner': 4.0.18 + '@vitest/snapshot': 4.0.18 + '@vitest/spy': 4.0.18 + '@vitest/utils': 4.0.18 + es-module-lexer: 1.7.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 4.0.0 + std-env: 3.10.0 tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2) + tinyrainbow: 3.0.3 + vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)(lightningcss@1.31.1)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 25.3.5 transitivePeerDependencies: + - jiti + - less + - lightningcss - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml web-namespaces@2.0.1: {} @@ -10269,7 +10390,7 @@ snapshots: xml2js@0.6.2: dependencies: - sax: 1.6.0 + sax: 1.5.0 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} @@ -10278,8 +10399,7 @@ snapshots: y18n@5.0.8: {} - yaml@2.8.2: - optional: true + yaml@2.8.2: {} yargs-parser@20.2.9: {}