From 7e0f177773fd34976f702a83ef15119df61882b9 Mon Sep 17 00:00:00 2001 From: sragss Date: Tue, 6 Jan 2026 18:47:05 -0500 Subject: [PATCH 01/20] feat(extensions): add Sign-In-With-X (SIWX) extension Implements CAIP-122 compliant wallet-based identity assertions for x402 v2. - Add SIWX types following CAIP-122 standard - Server-side: declareSIWxExtension() for 402 response generation - Client-side: createSIWxPayload() for proof creation and signing - Message construction following CAIP-122 format - Header encoding/parsing (base64 per x402 v2 spec) - Validation: temporal, domain binding, nonce checking - Verification: cryptographic signature validation via siwe library - JSON Schema generation for payload validation - Test coverage for core flows --- typescript/packages/extensions/package.json | 1 + .../extensions/src/sign-in-with-x/client.ts | 76 ++++++++ .../extensions/src/sign-in-with-x/declare.ts | 75 ++++++++ .../extensions/src/sign-in-with-x/encode.ts | 45 +++++ .../extensions/src/sign-in-with-x/index.ts | 99 +++++++++- .../extensions/src/sign-in-with-x/message.ts | 86 +++++++++ .../extensions/src/sign-in-with-x/parse.ts | 67 +++++++ .../extensions/src/sign-in-with-x/schema.ts | 34 ++++ .../extensions/src/sign-in-with-x/server.ts | 55 ++++++ .../extensions/src/sign-in-with-x/sign.ts | 64 ++++++ .../extensions/src/sign-in-with-x/types.ts | 174 +++++++++++++++++ .../extensions/src/sign-in-with-x/validate.ts | 141 ++++++++++++++ .../extensions/src/sign-in-with-x/verify.ts | 99 ++++++++++ .../extensions/test/sign-in-with-x.test.ts | 182 ++++++++++++++++++ typescript/pnpm-lock.yaml | 118 ++++++++++++ 15 files changed, 1311 insertions(+), 5 deletions(-) create mode 100644 typescript/packages/extensions/src/sign-in-with-x/client.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/declare.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/encode.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/message.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/parse.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/schema.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/server.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/sign.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/types.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/validate.ts create mode 100644 typescript/packages/extensions/src/sign-in-with-x/verify.ts create mode 100644 typescript/packages/extensions/test/sign-in-with-x.test.ts diff --git a/typescript/packages/extensions/package.json b/typescript/packages/extensions/package.json index 3678a5a69..dd944aa28 100644 --- a/typescript/packages/extensions/package.json +++ b/typescript/packages/extensions/package.json @@ -45,6 +45,7 @@ "dependencies": { "@x402/core": "workspace:*", "ajv": "^8.17.1", + "siwe": "^2.3.2", "zod": "^3.24.2" }, "exports": { diff --git a/typescript/packages/extensions/src/sign-in-with-x/client.ts b/typescript/packages/extensions/src/sign-in-with-x/client.ts new file mode 100644 index 000000000..f93a27eb5 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/client.ts @@ -0,0 +1,76 @@ +/** + * Complete client flow for SIWX extension + * + * Combines message construction, signing, and payload creation. + */ + +import type { SIWxExtensionInfo, SIWxPayload } from "./types"; +import type { SIWxSigner } from "./sign"; +import { createSIWxMessage } from "./message"; +import { signSIWxMessage } from "./sign"; + +/** + * Create a complete SIWX payload from server extension info. + * + * This function: + * 1. Extracts the wallet address from the signer + * 2. Constructs the CAIP-122 message + * 3. Signs the message + * 4. Returns the complete payload ready for encoding + * + * @param serverExtension - Server-provided extension info from PaymentRequired + * @param signer - Wallet or account that can sign messages + * @returns Complete SIWX payload with signature + * + * @example + * ```typescript + * // Get extension info from 402 response + * const serverInfo = paymentRequired.extensions['sign-in-with-x'].info; + * + * // Create signed payload + * const payload = await createSIWxPayload(serverInfo, wallet); + * + * // Encode for header + * const header = encodeSIWxHeader(payload); + * + * // Send authenticated request + * fetch(url, { headers: { 'SIGN-IN-WITH-X': header } }); + * ``` + */ +export async function createSIWxPayload( + serverExtension: SIWxExtensionInfo, + signer: SIWxSigner, +): Promise { + // Get address from signer + let address: string; + if (signer.account?.address) { + address = signer.account.address; + } else if (signer.address) { + address = signer.address; + } else { + throw new Error("Cannot determine signer address: no account or address property found"); + } + + // Construct CAIP-122 message + const message = createSIWxMessage(serverExtension, address); + + // Sign message + const signature = await signSIWxMessage(message, signer); + + // Return complete payload + return { + domain: serverExtension.domain, + address, + statement: serverExtension.statement, + uri: serverExtension.uri, + version: serverExtension.version, + chainId: serverExtension.chainId, + nonce: serverExtension.nonce, + issuedAt: serverExtension.issuedAt, + expirationTime: serverExtension.expirationTime, + notBefore: serverExtension.notBefore, + requestId: serverExtension.requestId, + resources: serverExtension.resources, + signature, + }; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/declare.ts b/typescript/packages/extensions/src/sign-in-with-x/declare.ts new file mode 100644 index 000000000..f0ef3bae1 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/declare.ts @@ -0,0 +1,75 @@ +/** + * Server-side declaration helper for SIWX extension + * + * Helps servers declare SIWX authentication requirements in PaymentRequired responses. + */ + +import { randomBytes } from "crypto"; +import type { SIWxExtension, SIWxExtensionInfo, DeclareSIWxOptions } from "./types"; +import { SIGN_IN_WITH_X } from "./types"; +import { buildSIWxSchema } from "./schema"; + +/** + * Create a SIWX extension declaration for PaymentRequired.extensions + * + * Auto-generates: + * - nonce: Cryptographically secure random string + * - issuedAt: Current timestamp in ISO 8601 format + * - domain: Extracted from resourceUri host + * - resources: Array containing resourceUri + * + * @param options - Configuration options + * @returns Extension object ready for PaymentRequired.extensions + * + * @example + * ```typescript + * const extensions = declareSIWxExtension({ + * resourceUri: 'https://api.example.com/data', + * network: 'eip155:8453', + * statement: 'Sign in to access your purchased content', + * }); + * + * // Include in PaymentRequired response + * const paymentRequired = { + * x402Version: 2, + * resource: { url: 'https://api.example.com/data', ... }, + * accepts: [...], + * extensions, + * }; + * ``` + */ +export function declareSIWxExtension(options: DeclareSIWxOptions): Record { + const url = new URL(options.resourceUri); + + // Auto-generate fields per spec + const nonce = randomBytes(16).toString("hex"); + const issuedAt = new Date().toISOString(); + const expirationTime = + options.expirationTime ?? new Date(Date.now() + 5 * 60 * 1000).toISOString(); + + const info: SIWxExtensionInfo = { + domain: url.host, + uri: options.resourceUri, + version: options.version ?? "1", + chainId: options.network, + nonce, + issuedAt, + expirationTime, + resources: [options.resourceUri], + }; + + // Add optional fields if provided + if (options.statement) { + info.statement = options.statement; + } + if (options.signatureScheme) { + info.signatureScheme = options.signatureScheme; + } + + return { + [SIGN_IN_WITH_X]: { + info, + schema: buildSIWxSchema(), + }, + }; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/encode.ts b/typescript/packages/extensions/src/sign-in-with-x/encode.ts new file mode 100644 index 000000000..3d0eb2cb8 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/encode.ts @@ -0,0 +1,45 @@ +/** + * Header encoding for SIWX extension + * + * Encodes SIWX payload for the SIGN-IN-WITH-X HTTP header. + * Per CHANGELOG-v2.md line 335: header should be base64-encoded. + */ + +import { safeBase64Encode } from "@x402/core/utils"; +import type { SIWxPayload } from "./types"; + +/** + * Encode SIWX payload for SIGN-IN-WITH-X header. + * + * Uses base64 encoding per x402 v2 spec (CHANGELOG-v2.md line 335). + * + * @param payload - Complete SIWX payload with signature + * @returns Base64-encoded JSON string + * + * @example + * ```typescript + * const payload = await createSIWxPayload(serverInfo, signer); + * const header = encodeSIWxHeader(payload); + * + * fetch(url, { + * headers: { 'SIGN-IN-WITH-X': header } + * }); + * ``` + */ +export function encodeSIWxHeader(payload: SIWxPayload): string { + return safeBase64Encode(JSON.stringify(payload)); +} + +/** + * Encode SIWX payload as raw JSON. + * + * For environments where base64 encoding is not required. + * Note: This is NOT spec-compliant but may be useful for debugging + * or legacy compatibility. + * + * @param payload - Complete SIWX payload with signature + * @returns JSON string (not base64 encoded) + */ +export function encodeSIWxHeaderRaw(payload: SIWxPayload): string { + return JSON.stringify(payload); +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 3c7df2dad..45a96832c 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -1,10 +1,99 @@ /** - * sign-in-with-x - x402 Payment Protocol Sign-In-With-X Extension + * Sign-In-With-X Extension for x402 v2 * - * This module provides Sign-In-With-X authentication capabilities for the x402 payment protocol. + * CAIP-122 compliant wallet authentication for payment-protected resources. + * Allows clients to prove control of a wallet that may have previously paid + * for a resource, enabling servers to grant access without requiring repurchase. + * + * ## Server Usage + * + * ```typescript + * import { + * declareSIWxExtension, + * parseSIWxHeader, + * validateSIWxMessage, + * verifySIWxSignature, + * SIGN_IN_WITH_X, + * } from '@x402/extensions/sign-in-with-x'; + * + * // 1. Declare auth requirement in PaymentRequired response + * const extensions = declareSIWxExtension({ + * resourceUri: 'https://api.example.com/data', + * network: 'eip155:8453', + * statement: 'Sign in to access your purchased content', + * }); + * + * // 2. Verify incoming proof + * const header = request.headers.get('SIGN-IN-WITH-X'); + * if (header) { + * const payload = parseSIWxHeader(header); + * + * const validation = await validateSIWxMessage( + * payload, + * 'https://api.example.com/data' + * ); + * + * if (validation.valid) { + * const verification = await verifySIWxSignature(payload); + * if (verification.valid) { + * // Authentication successful! + * // verification.address is the verified wallet + * } + * } + * } + * ``` + * + * ## Client Usage + * + * ```typescript + * import { + * createSIWxPayload, + * encodeSIWxHeader, + * } from '@x402/extensions/sign-in-with-x'; + * + * // 1. Get extension info from 402 response + * const serverInfo = paymentRequired.extensions['sign-in-with-x'].info; + * + * // 2. Create signed payload + * const payload = await createSIWxPayload(serverInfo, wallet); + * + * // 3. Encode for header + * const header = encodeSIWxHeader(payload); + * + * // 4. Send authenticated request + * fetch(url, { headers: { 'SIGN-IN-WITH-X': header } }); + * ``` + * + * @packageDocumentation */ -// Export Sign-In-With-X extension modules here -// The actual implementation logic will be added later +// Extension identifier and schema +export { SIGN_IN_WITH_X, SIWxPayloadSchema } from "./types"; + +// Types +export type { + SIWxExtension, + SIWxExtensionInfo, + SIWxExtensionSchema, + SIWxPayload, + DeclareSIWxOptions, + SignatureScheme, + SIWxValidationResult, + SIWxValidationOptions, + SIWxVerifyResult, + SIWxVerifyOptions, +} from "./types"; + +// Server exports +export { declareSIWxExtension } from "./declare"; +export { parseSIWxHeader } from "./parse"; +export { validateSIWxMessage } from "./validate"; +export { verifySIWxSignature } from "./verify"; +export { siwxResourceServerExtension } from "./server"; +export { buildSIWxSchema } from "./schema"; -export {}; +// Client exports +export { createSIWxMessage } from "./message"; +export { signSIWxMessage, type SIWxSigner, type SignOptions } from "./sign"; +export { createSIWxPayload } from "./client"; +export { encodeSIWxHeader, encodeSIWxHeaderRaw } from "./encode"; diff --git a/typescript/packages/extensions/src/sign-in-with-x/message.ts b/typescript/packages/extensions/src/sign-in-with-x/message.ts new file mode 100644 index 000000000..2b52ca4df --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/message.ts @@ -0,0 +1,86 @@ +/** + * CAIP-122 message construction for SIWX extension + * + * Constructs the canonical message string for signing. + * Per https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-122.md + */ + +import type { SIWxExtensionInfo } from "./types"; + +/** + * Construct CAIP-122 compliant message string for signing. + * + * The message format follows the EIP-4361 / CAIP-122 structure: + * + * ``` + * ${domain} wants you to sign in with your ${chainId} account: + * ${address} + * + * ${statement} + * + * URI: ${uri} + * Version: ${version} + * Chain ID: ${chainId} + * Nonce: ${nonce} + * Issued At: ${issuedAt} + * [Expiration Time: ${expirationTime}] + * [Not Before: ${notBefore}] + * [Request ID: ${requestId}] + * [Resources: + * - ${resource1} + * - ${resource2}] + * ``` + * + * @param serverInfo - Server-provided extension info + * @param address - Client wallet address + * @returns Message string ready for signing + * + * @example + * ```typescript + * const serverInfo = paymentRequired.extensions['sign-in-with-x'].info; + * const message = createSIWxMessage(serverInfo, wallet.address); + * const signature = await wallet.signMessage({ message }); + * ``` + */ +export function createSIWxMessage(serverInfo: SIWxExtensionInfo, address: string): string { + const lines: string[] = []; + + // Header + lines.push(`${serverInfo.domain} wants you to sign in with your ${serverInfo.chainId} account:`); + lines.push(address); + lines.push(""); + + // Statement (optional) + if (serverInfo.statement) { + lines.push(serverInfo.statement); + lines.push(""); + } + + // Required fields + lines.push(`URI: ${serverInfo.uri}`); + lines.push(`Version: ${serverInfo.version}`); + lines.push(`Chain ID: ${serverInfo.chainId}`); + lines.push(`Nonce: ${serverInfo.nonce}`); + lines.push(`Issued At: ${serverInfo.issuedAt}`); + + // Optional fields + if (serverInfo.expirationTime) { + lines.push(`Expiration Time: ${serverInfo.expirationTime}`); + } + if (serverInfo.notBefore) { + lines.push(`Not Before: ${serverInfo.notBefore}`); + } + if (serverInfo.requestId) { + lines.push(`Request ID: ${serverInfo.requestId}`); + } + + // Resources + if (serverInfo.resources && serverInfo.resources.length > 0) { + lines.push("Resources:"); + for (const resource of serverInfo.resources) { + lines.push(`- ${resource}`); + } + } + + return lines.join("\n"); +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/parse.ts b/typescript/packages/extensions/src/sign-in-with-x/parse.ts new file mode 100644 index 000000000..39e3d2460 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/parse.ts @@ -0,0 +1,67 @@ +/** + * Header parsing for SIWX extension + * + * Parses the SIGN-IN-WITH-X header from client requests. + * Supports both base64-encoded (spec) and raw JSON (backwards compat). + */ + +import { Base64EncodedRegex, safeBase64Decode } from "@x402/core/utils"; +import { SIWxPayloadSchema, type SIWxPayload } from "./types"; + +/** + * Parse SIGN-IN-WITH-X header into structured payload. + * + * Supports both: + * - Base64-encoded JSON (spec-compliant, per CHANGELOG-v2.md line 335) + * - Raw JSON (backwards compatibility) + * + * @param header - The SIGN-IN-WITH-X header value + * @returns Parsed SIWX payload + * @throws Error if header is invalid or missing required fields + * + * @example + * ```typescript + * const header = request.headers.get('SIGN-IN-WITH-X'); + * if (header) { + * const payload = parseSIWxHeader(header); + * // payload.address, payload.signature, etc. + * } + * ``` + */ +export function parseSIWxHeader(header: string): SIWxPayload { + let jsonStr: string; + + // Try base64 decode first (spec-compliant) + if (Base64EncodedRegex.test(header)) { + try { + jsonStr = safeBase64Decode(header); + } catch { + // If base64 decode fails, treat as raw JSON + jsonStr = header; + } + } else { + // Fall back to raw JSON (backwards compatibility) + jsonStr = header; + } + + // Parse JSON + let rawPayload: unknown; + try { + rawPayload = JSON.parse(jsonStr); + } catch (error) { + if (error instanceof SyntaxError) { + throw new Error("Invalid SIWX header: not valid JSON or base64-encoded JSON"); + } + throw error; + } + + // Validate with zod schema + const parsed = SIWxPayloadSchema.safeParse(rawPayload); + + if (!parsed.success) { + const issues = parsed.error.issues.map(i => `${i.path.join(".")}: ${i.message}`).join(", "); + throw new Error(`Invalid SIWX header: ${issues}`); + } + + return parsed.data; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/schema.ts b/typescript/packages/extensions/src/sign-in-with-x/schema.ts new file mode 100644 index 000000000..be067fe7f --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/schema.ts @@ -0,0 +1,34 @@ +/** + * JSON Schema builder for SIWX extension + * + * Per CHANGELOG-v2.md lines 276-292 + */ + +import type { SIWxExtensionSchema } from "./types"; + +/** + * Build JSON Schema for SIWX extension validation. + * This schema validates the client proof payload structure. + */ +export function buildSIWxSchema(): SIWxExtensionSchema { + return { + $schema: "https://json-schema.org/draft/2020-12/schema", + type: "object", + properties: { + domain: { type: "string" }, + address: { type: "string" }, + statement: { type: "string" }, + uri: { type: "string", format: "uri" }, + version: { type: "string" }, + chainId: { type: "string" }, + nonce: { type: "string" }, + issuedAt: { type: "string", format: "date-time" }, + expirationTime: { type: "string", format: "date-time" }, + notBefore: { type: "string", format: "date-time" }, + requestId: { type: "string" }, + resources: { type: "array", items: { type: "string", format: "uri" } }, + signature: { type: "string" }, + }, + required: ["domain", "address", "uri", "version", "chainId", "nonce", "issuedAt", "signature"], + }; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/server.ts b/typescript/packages/extensions/src/sign-in-with-x/server.ts new file mode 100644 index 000000000..4e4216971 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/server.ts @@ -0,0 +1,55 @@ +/** + * ResourceServerExtension hook for SIWX extension + * + * Optional hook for x402 resource server integration. + * Can enrich declarations based on transport context. + */ + +import { SIGN_IN_WITH_X } from "./types"; + +/** + * Extension declaration type for SIWX + */ +export interface SIWxDeclaration { + info: unknown; + schema: unknown; +} + +/** + * Transport context provided by the resource server + */ +export interface TransportContext { + /** HTTP request if using HTTP transport */ + request?: { headers: Headers; url: string }; +} + +/** + * SIWX ResourceServerExtension hook. + * + * Currently passes through declarations unchanged. + * Can be extended to auto-derive fields from HTTP context. + * + * @example + * ```typescript + * import { siwxResourceServerExtension } from '@x402/extensions/sign-in-with-x'; + * + * // Register with x402 resource server + * server.registerExtension(siwxResourceServerExtension); + * ``` + */ +export const siwxResourceServerExtension = { + key: SIGN_IN_WITH_X, + + /** + * Enrich SIWX declaration with transport context. + * Currently a pass-through; can be extended for auto-derivation. + */ + enrichDeclaration: ( + declaration: SIWxDeclaration, + _transportContext?: TransportContext, + ): SIWxDeclaration => { + // Pass through - server explicitly declares SIWX requirements + // Future: Could auto-derive domain from HTTP Host header + return declaration; + }, +}; diff --git a/typescript/packages/extensions/src/sign-in-with-x/sign.ts b/typescript/packages/extensions/src/sign-in-with-x/sign.ts new file mode 100644 index 000000000..b8afc31cc --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/sign.ts @@ -0,0 +1,64 @@ +/** + * Message signing for SIWX extension + * + * Client-side helper for signing SIWX messages. + */ + +/** + * Generic signer interface for SIWX message signing. + * Compatible with viem WalletClient and PrivateKeyAccount. + */ +export interface SIWxSigner { + /** Sign a message and return the signature */ + signMessage: (args: { message: string; account?: unknown }) => Promise; + /** Account object (for WalletClient) */ + account?: { address: string }; + /** Direct address (for PrivateKeyAccount) */ + address?: string; +} + +/** + * Sign options (currently unused, reserved for future schemes) + */ +export interface SignOptions { + // Future: signatureScheme for EIP-712, etc. +} + +/** + * Sign SIWX message with wallet. + * + * Compatible with: + * - viem WalletClient (browser wallets) + * - viem PrivateKeyAccount (server-side) + * + * @param message - CAIP-122 message string to sign + * @param signer - Wallet or account that can sign messages + * @param _options - Reserved for future use + * @returns Signature string + * + * @example + * ```typescript + * // With WalletClient (browser) + * const signature = await signSIWxMessage(message, walletClient); + * + * // With PrivateKeyAccount (server) + * const account = privateKeyToAccount('0x...'); + * const signature = await signSIWxMessage(message, account); + * ``` + */ +export async function signSIWxMessage( + message: string, + signer: SIWxSigner, + _options: SignOptions = {}, +): Promise { + // Check if signer has an account property (WalletClient pattern) + if (signer.account) { + return signer.signMessage({ + message, + account: signer.account, + }); + } + + // Direct signMessage (PrivateKeyAccount pattern) + return signer.signMessage({ message }); +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/types.ts b/typescript/packages/extensions/src/sign-in-with-x/types.ts new file mode 100644 index 000000000..6197a2759 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/types.ts @@ -0,0 +1,174 @@ +/** + * Type definitions for the Sign-In-With-X (SIWX) extension + * + * Implements CAIP-122 standard for chain-agnostic wallet-based identity assertions. + * Per x402 v2 spec: typescript/site/CHANGELOG-v2.md lines 237-341 + */ + +import { z } from "zod"; + +/** + * Extension identifier constant + */ +export const SIGN_IN_WITH_X = "sign-in-with-x"; + +/** + * Supported signature schemes per CHANGELOG-v2.md line 271 + */ +export type SignatureScheme = + | "eip191" // personal_sign (default for EVM EOAs) + | "eip712" // typed data signing + | "eip1271" // smart contract wallet verification + | "eip6492" // counterfactual smart wallet verification + | "siws" // Sign-In-With-Solana + | "sep10"; // Stellar SEP-10 + +/** + * Server-declared extension info included in PaymentRequired.extensions + * Per CHANGELOG-v2.md lines 263-272 + */ +export interface SIWxExtensionInfo { + /** Server's domain (derived from resourceUri host) */ + domain: string; + /** Full resource URI */ + uri: string; + /** Human-readable purpose for signing */ + statement?: string; + /** CAIP-122 version, always "1" */ + version: string; + /** CAIP-2 chain identifier (e.g., "eip155:8453") */ + chainId: string; + /** Cryptographic nonce (SDK auto-generates) */ + nonce: string; + /** ISO 8601 timestamp (SDK auto-generates) */ + issuedAt: string; + /** Optional expiry (default: +5 min) */ + expirationTime?: string; + /** Optional validity start */ + notBefore?: string; + /** Optional correlation ID */ + requestId?: string; + /** Associated resources */ + resources?: string[]; + /** Signature scheme hint */ + signatureScheme?: SignatureScheme; +} + +/** + * JSON Schema for SIWX extension validation + * Per CHANGELOG-v2.md lines 276-292 + */ +export interface SIWxExtensionSchema { + $schema: string; + type: "object"; + properties: { + domain: { type: "string" }; + address: { type: "string" }; + statement?: { type: "string" }; + uri: { type: "string"; format: "uri" }; + version: { type: "string" }; + chainId: { type: "string" }; + nonce: { type: "string" }; + issuedAt: { type: "string"; format: "date-time" }; + expirationTime?: { type: "string"; format: "date-time" }; + notBefore?: { type: "string"; format: "date-time" }; + requestId?: { type: "string" }; + resources?: { type: "array"; items: { type: "string"; format: "uri" } }; + signature: { type: "string" }; + }; + required: string[]; +} + +/** + * Complete SIWX extension structure (info + schema) + * Follows standard x402 v2 extension pattern + */ +export interface SIWxExtension { + info: SIWxExtensionInfo; + schema: SIWxExtensionSchema; +} + +/** + * Zod schema for SIWX payload validation + * Client proof payload sent in SIGN-IN-WITH-X header + * Per CHANGELOG-v2.md lines 301-315 + */ +export const SIWxPayloadSchema = z.object({ + domain: z.string(), + address: z.string(), + statement: z.string().optional(), + uri: z.string(), + version: z.string(), + chainId: z.string(), + nonce: z.string(), + issuedAt: z.string(), + expirationTime: z.string().optional(), + notBefore: z.string().optional(), + requestId: z.string().optional(), + resources: z.array(z.string()).optional(), + signatureScheme: z + .enum(["eip191", "eip712", "eip1271", "eip6492", "siws", "sep10"]) + .optional(), + signature: z.string(), +}); + +/** + * Client proof payload type (inferred from zod schema) + */ +export type SIWxPayload = z.infer; + +/** + * Options for declaring SIWX extension on server + */ +export interface DeclareSIWxOptions { + /** Full resource URI (domain derived from this) */ + resourceUri: string; + /** Human-readable purpose */ + statement?: string; + /** CAIP-122 version (default: "1") */ + version?: string; + /** CAIP-2 network identifier (e.g., "eip155:8453") */ + network: string; + /** Optional explicit expiration time */ + expirationTime?: string; + /** Signature scheme hint */ + signatureScheme?: SignatureScheme; +} + +/** + * Validation result from validateSIWxMessage + */ +export interface SIWxValidationResult { + valid: boolean; + error?: string; +} + +/** + * Options for message validation + */ +export interface SIWxValidationOptions { + /** Maximum age for issuedAt in milliseconds (default: 5 minutes) */ + maxAge?: number; + /** Custom nonce validation function */ + checkNonce?: (nonce: string) => boolean | Promise; +} + +/** + * Result from signature verification + */ +export interface SIWxVerifyResult { + valid: boolean; + /** Recovered/verified address (checksummed) */ + address?: string; + error?: string; +} + +/** + * Options for signature verification + */ +export interface SIWxVerifyOptions { + /** Web3 provider for EIP-1271/6492 smart wallet verification */ + provider?: unknown; + /** Enable smart wallet verification */ + checkSmartWallet?: boolean; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/validate.ts b/typescript/packages/extensions/src/sign-in-with-x/validate.ts new file mode 100644 index 000000000..2a684924c --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/validate.ts @@ -0,0 +1,141 @@ +/** + * Message validation for SIWX extension + * + * Validates SIWX payload fields before cryptographic verification. + * Per CHANGELOG-v2.md validation rules (lines 318-329). + */ + +import type { SIWxPayload, SIWxValidationResult, SIWxValidationOptions } from "./types"; + +/** Default maximum age for issuedAt: 5 minutes per spec */ +const DEFAULT_MAX_AGE_MS = 5 * 60 * 1000; + +/** Allow 1 minute clock skew for future timestamps */ +const CLOCK_SKEW_MS = 60 * 1000; + +/** + * Validate SIWX message fields. + * + * Performs validation per spec (CHANGELOG-v2.md lines 318-329): + * - Domain binding: domain MUST match server's domain + * - URI validation: uri must refer to base url of resource + * - Temporal validation: + * - issuedAt MUST be recent (< 5 minutes by default) + * - expirationTime MUST be in the future + * - notBefore (if present) MUST be in the past + * - Nonce: MUST be unique (via optional checkNonce callback) + * + * @param message - The SIWX payload to validate + * @param expectedResourceUri - Expected resource URI (for domain/URI matching) + * @param options - Validation options + * @returns Validation result + * + * @example + * ```typescript + * const payload = parseSIWxHeader(header); + * const result = await validateSIWxMessage( + * payload, + * 'https://api.example.com/data', + * { checkNonce: (n) => !usedNonces.has(n) } + * ); + * + * if (!result.valid) { + * return { error: result.error }; + * } + * ``` + */ +export async function validateSIWxMessage( + message: SIWxPayload, + expectedResourceUri: string, + options: SIWxValidationOptions = {}, +): Promise { + const expectedUrl = new URL(expectedResourceUri); + const maxAge = options.maxAge ?? DEFAULT_MAX_AGE_MS; + + // 1. Domain binding (spec: "domain field MUST match server's domain") + if (message.domain !== expectedUrl.host) { + return { + valid: false, + error: `Domain mismatch: expected "${expectedUrl.host}", got "${message.domain}"`, + }; + } + + // 2. URI validation (spec: "uri and resources must refer to base url of resource") + // Allow the message URI to be the origin or the full resource URL + if (!message.uri.startsWith(expectedUrl.origin)) { + return { + valid: false, + error: `URI mismatch: expected origin "${expectedUrl.origin}", got "${message.uri}"`, + }; + } + + // 3. issuedAt validation (spec: "MUST be recent, recommended < 5 minutes") + const issuedAt = new Date(message.issuedAt); + if (isNaN(issuedAt.getTime())) { + return { + valid: false, + error: "Invalid issuedAt timestamp", + }; + } + + const age = Date.now() - issuedAt.getTime(); + if (age > maxAge) { + return { + valid: false, + error: `Message too old: ${Math.round(age / 1000)}s exceeds ${maxAge / 1000}s limit`, + }; + } + if (age < -CLOCK_SKEW_MS) { + return { + valid: false, + error: "issuedAt is in the future (beyond clock skew tolerance)", + }; + } + + // 4. expirationTime validation (spec: "MUST be in the future") + if (message.expirationTime) { + const expiration = new Date(message.expirationTime); + if (isNaN(expiration.getTime())) { + return { + valid: false, + error: "Invalid expirationTime timestamp", + }; + } + if (expiration < new Date()) { + return { + valid: false, + error: "Message expired", + }; + } + } + + // 5. notBefore validation (spec: "if present, MUST be in the past") + if (message.notBefore) { + const notBefore = new Date(message.notBefore); + if (isNaN(notBefore.getTime())) { + return { + valid: false, + error: "Invalid notBefore timestamp", + }; + } + if (new Date() < notBefore) { + return { + valid: false, + error: "Message not yet valid (notBefore is in the future)", + }; + } + } + + // 6. Nonce validation (spec: "MUST be unique per session to prevent replay attacks") + if (options.checkNonce) { + const nonceValid = await options.checkNonce(message.nonce); + if (!nonceValid) { + return { + valid: false, + error: "Nonce validation failed (possible replay attack)", + }; + } + } + + return { valid: true }; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts new file mode 100644 index 000000000..960c2be16 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -0,0 +1,99 @@ +/** + * Signature verification for SIWX extension + * + * Cryptographically verifies SIWX signatures. + * Currently supports EVM (eip191). Extensible for other schemes. + */ + +import { SiweMessage } from "siwe"; +import type { SIWxPayload, SIWxVerifyResult, SIWxVerifyOptions } from "./types"; + +/** + * Verify SIWX signature cryptographically. + * + * Reconstructs the SIWE message from payload fields and verifies + * the signature matches the claimed address. + * + * @param payload - The SIWX payload containing signature + * @param options - Verification options + * @returns Verification result with recovered address if valid + * + * @example + * ```typescript + * const payload = parseSIWxHeader(header); + * const result = await verifySIWxSignature(payload); + * + * if (result.valid) { + * console.log('Verified wallet:', result.address); + * } else { + * console.error('Verification failed:', result.error); + * } + * ``` + */ +export async function verifySIWxSignature( + payload: SIWxPayload, + options: SIWxVerifyOptions = {}, +): Promise { + try { + // Parse CAIP-2 chainId (e.g., "eip155:8453" -> 8453) + const chainIdMatch = /^eip155:(\d+)$/.exec(payload.chainId); + if (!chainIdMatch) { + // TODO: Add support for solana:*, cosmos:*, etc. + return { + valid: false, + error: `Unsupported chainId namespace: ${payload.chainId}. Currently only eip155:* is supported.`, + }; + } + const numericChainId = parseInt(chainIdMatch[1], 10); + + // Reconstruct SIWE message for verification + const siweMessage = new SiweMessage({ + domain: payload.domain, + address: payload.address, + statement: payload.statement, + uri: payload.uri, + version: payload.version, + chainId: numericChainId, + nonce: payload.nonce, + issuedAt: payload.issuedAt, + expirationTime: payload.expirationTime, + notBefore: payload.notBefore, + requestId: payload.requestId, + resources: payload.resources, + }); + + // Verify signature + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const verifyOptions: any = { + signature: payload.signature, + }; + + // Add provider for smart wallet verification if enabled + if (options.checkSmartWallet && options.provider) { + verifyOptions.provider = options.provider; + } + + const result = await siweMessage.verify(verifyOptions); + + if (!result.success) { + // SiweError type - extract error details + const errorMessage = result.error + ? String(result.error) + : "Signature verification failed"; + return { + valid: false, + error: errorMessage, + }; + } + + return { + valid: true, + address: siweMessage.address, + }; + } catch (error) { + return { + valid: false, + error: error instanceof Error ? error.message : "Verification failed", + }; + } +} diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts new file mode 100644 index 000000000..ecc00ae34 --- /dev/null +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -0,0 +1,182 @@ +/** + * Tests for Sign-In-With-X Extension + */ + +import { describe, it, expect } from "vitest"; +import { + SIGN_IN_WITH_X, + SIWxPayloadSchema, + parseSIWxHeader, + encodeSIWxHeader, + encodeSIWxHeaderRaw, + declareSIWxExtension, + validateSIWxMessage, + createSIWxMessage, +} from "../src/sign-in-with-x/index"; +import { safeBase64Encode } from "@x402/core/utils"; + +const validPayload = { + domain: "api.example.com", + address: "0x1234567890123456789012345678901234567890", + statement: "Sign in to access your content", + uri: "https://api.example.com/data", + version: "1", + chainId: "eip155:8453", + nonce: "abc123def456", + issuedAt: new Date().toISOString(), + expirationTime: new Date(Date.now() + 5 * 60 * 1000).toISOString(), + resources: ["https://api.example.com/data"], + signature: "0xabcdef1234567890", +}; + +describe("Sign-In-With-X Extension", () => { + describe("SIGN_IN_WITH_X constant", () => { + it("should export the correct extension identifier", () => { + expect(SIGN_IN_WITH_X).toBe("sign-in-with-x"); + }); + }); + + describe("SIWxPayloadSchema", () => { + it("should validate a correct payload", () => { + const result = SIWxPayloadSchema.safeParse(validPayload); + expect(result.success).toBe(true); + }); + + it("should reject payload missing required fields", () => { + const invalidPayload = { domain: "example.com" }; + const result = SIWxPayloadSchema.safeParse(invalidPayload); + expect(result.success).toBe(false); + }); + + it("should accept payload with optional fields omitted", () => { + const minimalPayload = { + domain: "api.example.com", + address: "0x1234567890123456789012345678901234567890", + uri: "https://api.example.com", + version: "1", + chainId: "eip155:8453", + nonce: "abc123", + issuedAt: new Date().toISOString(), + signature: "0xabcdef", + }; + const result = SIWxPayloadSchema.safeParse(minimalPayload); + expect(result.success).toBe(true); + }); + }); + + describe("parseSIWxHeader", () => { + it("should parse base64-encoded header", () => { + const encoded = safeBase64Encode(JSON.stringify(validPayload)); + const parsed = parseSIWxHeader(encoded); + expect(parsed.domain).toBe(validPayload.domain); + expect(parsed.address).toBe(validPayload.address); + expect(parsed.signature).toBe(validPayload.signature); + }); + + it("should parse raw JSON header (backwards compatibility)", () => { + const raw = JSON.stringify(validPayload); + const parsed = parseSIWxHeader(raw); + expect(parsed.domain).toBe(validPayload.domain); + expect(parsed.signature).toBe(validPayload.signature); + }); + + it("should throw on invalid JSON", () => { + expect(() => parseSIWxHeader("not-valid-json")).toThrow("Invalid SIWX header"); + }); + + it("should throw on missing required fields", () => { + const incomplete = JSON.stringify({ domain: "example.com" }); + expect(() => parseSIWxHeader(incomplete)).toThrow("Invalid SIWX header"); + }); + }); + + describe("encodeSIWxHeader", () => { + it("should encode payload as base64", () => { + const encoded = encodeSIWxHeader(validPayload); + expect(() => Buffer.from(encoded, "base64")).not.toThrow(); + const decoded = JSON.parse(Buffer.from(encoded, "base64").toString("utf-8")); + expect(decoded.domain).toBe(validPayload.domain); + }); + }); + + describe("encodeSIWxHeaderRaw", () => { + it("should encode payload as raw JSON", () => { + const encoded = encodeSIWxHeaderRaw(validPayload); + const decoded = JSON.parse(encoded); + expect(decoded.domain).toBe(validPayload.domain); + }); + }); + + describe("declareSIWxExtension", () => { + it("should create extension with auto-generated fields", () => { + const result = declareSIWxExtension({ + resourceUri: "https://api.example.com/data", + network: "eip155:8453", + statement: "Sign in to access", + }); + + expect(result).toHaveProperty("sign-in-with-x"); + const extension = result["sign-in-with-x"]; + expect(extension.info.domain).toBe("api.example.com"); + expect(extension.info.uri).toBe("https://api.example.com/data"); + expect(extension.info.chainId).toBe("eip155:8453"); + expect(extension.info.nonce).toBeDefined(); + expect(extension.info.nonce.length).toBe(32); + expect(extension.info.issuedAt).toBeDefined(); + expect(extension.schema).toBeDefined(); + }); + }); + + describe("validateSIWxMessage", () => { + it("should validate correct message", async () => { + const now = new Date(); + const payload = { + ...validPayload, + issuedAt: now.toISOString(), + expirationTime: new Date(now.getTime() + 5 * 60 * 1000).toISOString(), + }; + + const result = await validateSIWxMessage(payload, "https://api.example.com/data"); + expect(result.valid).toBe(true); + }); + + it("should reject domain mismatch", async () => { + const result = await validateSIWxMessage(validPayload, "https://different.example.com/data"); + expect(result.valid).toBe(false); + expect(result.error).toContain("Domain mismatch"); + }); + }); + + describe("createSIWxMessage", () => { + it("should create CAIP-122 format message", () => { + const serverInfo = { + domain: "api.example.com", + uri: "https://api.example.com", + statement: "Sign in to access", + version: "1", + chainId: "eip155:8453", + nonce: "abc123", + issuedAt: "2024-01-01T00:00:00Z", + resources: ["https://api.example.com"], + }; + + const message = createSIWxMessage(serverInfo, "0x1234567890123456789012345678901234567890"); + + expect(message).toContain("api.example.com wants you to sign in"); + expect(message).toContain("eip155:8453"); + expect(message).toContain("0x1234567890123456789012345678901234567890"); + expect(message).toContain("Nonce: abc123"); + }); + }); + + describe("Integration - encode/parse roundtrip", () => { + it("should roundtrip through encode and parse", () => { + const encoded = encodeSIWxHeader(validPayload); + const parsed = parseSIWxHeader(encoded); + + expect(parsed.domain).toBe(validPayload.domain); + expect(parsed.address).toBe(validPayload.address); + expect(parsed.signature).toBe(validPayload.signature); + }); + }); +}); diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml index dc42045d8..9fb342c9d 100644 --- a/typescript/pnpm-lock.yaml +++ b/typescript/pnpm-lock.yaml @@ -78,6 +78,9 @@ importers: ajv: specifier: ^8.17.1 version: 8.17.1 + siwe: + specifier: ^2.3.2 + version: 2.3.2(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) zod: specifier: ^3.24.2 version: 3.25.76 @@ -1252,6 +1255,9 @@ importers: packages: + '@adraffy/ens-normalize@1.10.1': + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + '@adraffy/ens-normalize@1.11.0': resolution: {integrity: sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==} @@ -2495,6 +2501,9 @@ packages: resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + '@noble/curves@1.4.2': resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} @@ -2514,6 +2523,10 @@ packages: resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} @@ -3515,6 +3528,21 @@ packages: '@solana/web3.js@1.98.4': resolution: {integrity: sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==} + '@spruceid/siwe-parser@2.1.2': + resolution: {integrity: sha512-d/r3S1LwJyMaRAKQ0awmo9whfXeE88Qt00vRj91q5uv5ATtWIQEGJ67Yr5eSZw5zp1/fZCXZYuEckt8lSkereQ==} + + '@stablelib/binary@1.0.1': + resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} + + '@stablelib/int@1.0.1': + resolution: {integrity: sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==} + + '@stablelib/random@1.0.2': + resolution: {integrity: sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==} + + '@stablelib/wipe@1.0.1': + resolution: {integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} engines: {node: '>=14'} @@ -3750,6 +3778,9 @@ packages: '@types/node@22.18.0': resolution: {integrity: sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==} + '@types/node@22.7.5': + resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} + '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -4228,6 +4259,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + agent-base@7.1.4: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} @@ -4265,6 +4299,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + apg-js@4.4.0: + resolution: {integrity: sha512-fefmXFknJmtgtNEXfPwZKYkMFX4Fyeyz+fNF6JWp87biGOPslJbCBVU158zvKRZfHBKnJDy8CMM40oLFGkXT8Q==} + are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} @@ -5090,6 +5127,10 @@ packages: ethereum-cryptography@2.2.1: resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} + ethers@6.16.0: + resolution: {integrity: sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==} + engines: {node: '>=14.0.0'} + eventemitter2@6.4.9: resolution: {integrity: sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==} @@ -6616,6 +6657,11 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + siwe@2.3.2: + resolution: {integrity: sha512-aSf+6+Latyttbj5nMu6GF3doMfv2UYj83hhwZgUF20ky6fTS83uVhkQABdIVnEuS8y1bBdk7p6ltb9SmlhTTlA==} + peerDependencies: + ethers: ^5.6.8 || ^6.0.8 + snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} @@ -6902,6 +6948,9 @@ packages: tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -7012,6 +7061,9 @@ packages: uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -7144,6 +7196,9 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + valid-url@1.0.9: + resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==} + valtio@1.13.2: resolution: {integrity: sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==} engines: {node: '>=12.20.0'} @@ -7512,6 +7567,8 @@ packages: snapshots: + '@adraffy/ens-normalize@1.10.1': {} + '@adraffy/ens-normalize@1.11.0': {} '@alloc/quick-lru@5.2.0': {} @@ -9159,6 +9216,10 @@ snapshots: '@noble/ciphers@1.3.0': {} + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + '@noble/curves@1.4.2': dependencies: '@noble/hashes': 1.4.0 @@ -9179,6 +9240,8 @@ snapshots: dependencies: '@noble/hashes': 1.8.0 + '@noble/hashes@1.3.2': {} + '@noble/hashes@1.4.0': {} '@noble/hashes@1.7.0': {} @@ -11155,6 +11218,26 @@ snapshots: - typescript - utf-8-validate + '@spruceid/siwe-parser@2.1.2': + dependencies: + '@noble/hashes': 1.8.0 + apg-js: 4.4.0 + uri-js: 4.4.1 + valid-url: 1.0.9 + + '@stablelib/binary@1.0.1': + dependencies: + '@stablelib/int': 1.0.1 + + '@stablelib/int@1.0.1': {} + + '@stablelib/random@1.0.2': + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + + '@stablelib/wipe@1.0.1': {} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.3)': dependencies: '@babel/core': 7.28.3 @@ -11396,6 +11479,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@22.7.5': + dependencies: + undici-types: 6.19.8 + '@types/qs@6.14.0': {} '@types/range-parser@1.2.7': {} @@ -12535,6 +12622,8 @@ snapshots: acorn@8.15.0: {} + aes-js@4.0.0-beta.5: {} + agent-base@7.1.4: {} agentkeepalive@4.6.0: @@ -12572,6 +12661,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + apg-js@4.4.0: {} + are-docs-informative@0.0.2: {} argparse@2.0.1: {} @@ -13592,6 +13683,19 @@ snapshots: '@scure/bip32': 1.4.0 '@scure/bip39': 1.3.0 + ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 22.7.5 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + eventemitter2@6.4.9: {} eventemitter3@5.0.1: {} @@ -15234,6 +15338,14 @@ snapshots: signal-exit@4.1.0: {} + siwe@2.3.2(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + dependencies: + '@spruceid/siwe-parser': 2.1.2 + '@stablelib/random': 1.0.2 + ethers: 6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + uri-js: 4.4.1 + valid-url: 1.0.9 + snake-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -15527,6 +15639,8 @@ snapshots: tslib@1.14.1: {} + tslib@2.7.0: {} + tslib@2.8.1: {} tsup@8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1): @@ -15661,6 +15775,8 @@ snapshots: uncrypto@0.1.3: {} + undici-types@6.19.8: {} + undici-types@6.21.0: {} undici-types@7.16.0: {} @@ -15762,6 +15878,8 @@ snapshots: uuid@9.0.1: {} + valid-url@1.0.9: {} + valtio@1.13.2(@types/react@19.1.12)(react@19.2.1): dependencies: derive-valtio: 0.1.0(valtio@1.13.2(@types/react@19.1.12)(react@19.2.1)) From 4daa0f8ba98a09b9f5cac6b880636c2205d3c0ca Mon Sep 17 00:00:00 2001 From: sragss Date: Tue, 6 Jan 2026 18:58:52 -0500 Subject: [PATCH 02/20] simplify; remove future looking code --- .../extensions/src/sign-in-with-x/index.ts | 3 +- .../extensions/src/sign-in-with-x/server.ts | 55 ------------------- .../extensions/src/sign-in-with-x/sign.ts | 9 --- .../extensions/src/sign-in-with-x/verify.ts | 14 ++--- 4 files changed, 8 insertions(+), 73 deletions(-) delete mode 100644 typescript/packages/extensions/src/sign-in-with-x/server.ts diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 45a96832c..746cc6aad 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -89,11 +89,10 @@ export { declareSIWxExtension } from "./declare"; export { parseSIWxHeader } from "./parse"; export { validateSIWxMessage } from "./validate"; export { verifySIWxSignature } from "./verify"; -export { siwxResourceServerExtension } from "./server"; export { buildSIWxSchema } from "./schema"; // Client exports export { createSIWxMessage } from "./message"; -export { signSIWxMessage, type SIWxSigner, type SignOptions } from "./sign"; +export { signSIWxMessage, type SIWxSigner } from "./sign"; export { createSIWxPayload } from "./client"; export { encodeSIWxHeader, encodeSIWxHeaderRaw } from "./encode"; diff --git a/typescript/packages/extensions/src/sign-in-with-x/server.ts b/typescript/packages/extensions/src/sign-in-with-x/server.ts deleted file mode 100644 index 4e4216971..000000000 --- a/typescript/packages/extensions/src/sign-in-with-x/server.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * ResourceServerExtension hook for SIWX extension - * - * Optional hook for x402 resource server integration. - * Can enrich declarations based on transport context. - */ - -import { SIGN_IN_WITH_X } from "./types"; - -/** - * Extension declaration type for SIWX - */ -export interface SIWxDeclaration { - info: unknown; - schema: unknown; -} - -/** - * Transport context provided by the resource server - */ -export interface TransportContext { - /** HTTP request if using HTTP transport */ - request?: { headers: Headers; url: string }; -} - -/** - * SIWX ResourceServerExtension hook. - * - * Currently passes through declarations unchanged. - * Can be extended to auto-derive fields from HTTP context. - * - * @example - * ```typescript - * import { siwxResourceServerExtension } from '@x402/extensions/sign-in-with-x'; - * - * // Register with x402 resource server - * server.registerExtension(siwxResourceServerExtension); - * ``` - */ -export const siwxResourceServerExtension = { - key: SIGN_IN_WITH_X, - - /** - * Enrich SIWX declaration with transport context. - * Currently a pass-through; can be extended for auto-derivation. - */ - enrichDeclaration: ( - declaration: SIWxDeclaration, - _transportContext?: TransportContext, - ): SIWxDeclaration => { - // Pass through - server explicitly declares SIWX requirements - // Future: Could auto-derive domain from HTTP Host header - return declaration; - }, -}; diff --git a/typescript/packages/extensions/src/sign-in-with-x/sign.ts b/typescript/packages/extensions/src/sign-in-with-x/sign.ts index b8afc31cc..8aa5fece4 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/sign.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/sign.ts @@ -17,13 +17,6 @@ export interface SIWxSigner { address?: string; } -/** - * Sign options (currently unused, reserved for future schemes) - */ -export interface SignOptions { - // Future: signatureScheme for EIP-712, etc. -} - /** * Sign SIWX message with wallet. * @@ -33,7 +26,6 @@ export interface SignOptions { * * @param message - CAIP-122 message string to sign * @param signer - Wallet or account that can sign messages - * @param _options - Reserved for future use * @returns Signature string * * @example @@ -49,7 +41,6 @@ export interface SignOptions { export async function signSIWxMessage( message: string, signer: SIWxSigner, - _options: SignOptions = {}, ): Promise { // Check if signer has an account property (WalletClient pattern) if (signer.account) { diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index 960c2be16..d66aec1c7 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -5,7 +5,7 @@ * Currently supports EVM (eip191). Extensible for other schemes. */ -import { SiweMessage } from "siwe"; +import { SiweMessage, type VerifyParams, type VerifyOpts } from "siwe"; import type { SIWxPayload, SIWxVerifyResult, SIWxVerifyOptions } from "./types"; /** @@ -63,17 +63,17 @@ export async function verifySIWxSignature( }); // Verify signature - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const verifyOptions: any = { + const verifyParams: VerifyParams = { signature: payload.signature, }; // Add provider for smart wallet verification if enabled - if (options.checkSmartWallet && options.provider) { - verifyOptions.provider = options.provider; - } + const verifyOpts: VerifyOpts | undefined = + options.checkSmartWallet && options.provider + ? { provider: options.provider } + : undefined; - const result = await siweMessage.verify(verifyOptions); + const result = await siweMessage.verify(verifyParams, verifyOpts); if (!result.success) { // SiweError type - extract error details From dc96f84ecf2dd12790d9d455f9678ca743c5463c Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 11:11:06 -0500 Subject: [PATCH 03/20] add integration test --- typescript/packages/extensions/package.json | 1 + .../extensions/src/sign-in-with-x/message.ts | 87 +++---- .../extensions/test/sign-in-with-x.test.ts | 51 ++++- typescript/pnpm-lock.yaml | 213 ++++++++++++------ 4 files changed, 216 insertions(+), 136 deletions(-) diff --git a/typescript/packages/extensions/package.json b/typescript/packages/extensions/package.json index dd944aa28..f53a94535 100644 --- a/typescript/packages/extensions/package.json +++ b/typescript/packages/extensions/package.json @@ -38,6 +38,7 @@ "tsup": "^8.4.0", "tsx": "^4.19.2", "typescript": "^5.7.3", + "viem": "^2.43.5", "vite": "^6.2.6", "vite-tsconfig-paths": "^5.1.4", "vitest": "^3.0.5" diff --git a/typescript/packages/extensions/src/sign-in-with-x/message.ts b/typescript/packages/extensions/src/sign-in-with-x/message.ts index 2b52ca4df..1698025da 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/message.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/message.ts @@ -2,34 +2,16 @@ * CAIP-122 message construction for SIWX extension * * Constructs the canonical message string for signing. - * Per https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-122.md + * Uses siwe library for EIP-4361 compliant message format. */ +import { SiweMessage } from "siwe"; import type { SIWxExtensionInfo } from "./types"; /** - * Construct CAIP-122 compliant message string for signing. + * Construct EIP-4361 compliant message string for signing. * - * The message format follows the EIP-4361 / CAIP-122 structure: - * - * ``` - * ${domain} wants you to sign in with your ${chainId} account: - * ${address} - * - * ${statement} - * - * URI: ${uri} - * Version: ${version} - * Chain ID: ${chainId} - * Nonce: ${nonce} - * Issued At: ${issuedAt} - * [Expiration Time: ${expirationTime}] - * [Not Before: ${notBefore}] - * [Request ID: ${requestId}] - * [Resources: - * - ${resource1} - * - ${resource2}] - * ``` + * Uses the siwe library to ensure message format matches verification. * * @param serverInfo - Server-provided extension info * @param address - Client wallet address @@ -43,44 +25,27 @@ import type { SIWxExtensionInfo } from "./types"; * ``` */ export function createSIWxMessage(serverInfo: SIWxExtensionInfo, address: string): string { - const lines: string[] = []; - - // Header - lines.push(`${serverInfo.domain} wants you to sign in with your ${serverInfo.chainId} account:`); - lines.push(address); - lines.push(""); - - // Statement (optional) - if (serverInfo.statement) { - lines.push(serverInfo.statement); - lines.push(""); + // Parse CAIP-2 chainId (e.g., "eip155:8453" -> 8453) + const chainIdMatch = /^eip155:(\d+)$/.exec(serverInfo.chainId); + if (!chainIdMatch) { + throw new Error(`Unsupported chainId format: ${serverInfo.chainId}. Expected eip155:`); } - - // Required fields - lines.push(`URI: ${serverInfo.uri}`); - lines.push(`Version: ${serverInfo.version}`); - lines.push(`Chain ID: ${serverInfo.chainId}`); - lines.push(`Nonce: ${serverInfo.nonce}`); - lines.push(`Issued At: ${serverInfo.issuedAt}`); - - // Optional fields - if (serverInfo.expirationTime) { - lines.push(`Expiration Time: ${serverInfo.expirationTime}`); - } - if (serverInfo.notBefore) { - lines.push(`Not Before: ${serverInfo.notBefore}`); - } - if (serverInfo.requestId) { - lines.push(`Request ID: ${serverInfo.requestId}`); - } - - // Resources - if (serverInfo.resources && serverInfo.resources.length > 0) { - lines.push("Resources:"); - for (const resource of serverInfo.resources) { - lines.push(`- ${resource}`); - } - } - - return lines.join("\n"); + const numericChainId = parseInt(chainIdMatch[1], 10); + + const siweMessage = new SiweMessage({ + domain: serverInfo.domain, + address, + statement: serverInfo.statement, + uri: serverInfo.uri, + version: serverInfo.version, + chainId: numericChainId, + nonce: serverInfo.nonce, + issuedAt: serverInfo.issuedAt, + expirationTime: serverInfo.expirationTime, + notBefore: serverInfo.notBefore, + requestId: serverInfo.requestId, + resources: serverInfo.resources, + }); + + return siweMessage.prepareMessage(); } diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts index ecc00ae34..540dee93e 100644 --- a/typescript/packages/extensions/test/sign-in-with-x.test.ts +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -12,8 +12,11 @@ import { declareSIWxExtension, validateSIWxMessage, createSIWxMessage, + createSIWxPayload, + verifySIWxSignature, } from "../src/sign-in-with-x/index"; import { safeBase64Encode } from "@x402/core/utils"; +import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; const validPayload = { domain: "api.example.com", @@ -148,24 +151,24 @@ describe("Sign-In-With-X Extension", () => { }); describe("createSIWxMessage", () => { - it("should create CAIP-122 format message", () => { + it("should create EIP-4361 format message", () => { const serverInfo = { domain: "api.example.com", uri: "https://api.example.com", statement: "Sign in to access", version: "1", chainId: "eip155:8453", - nonce: "abc123", - issuedAt: "2024-01-01T00:00:00Z", + nonce: "abc12345def67890", + issuedAt: "2024-01-01T00:00:00.000Z", resources: ["https://api.example.com"], }; const message = createSIWxMessage(serverInfo, "0x1234567890123456789012345678901234567890"); expect(message).toContain("api.example.com wants you to sign in"); - expect(message).toContain("eip155:8453"); expect(message).toContain("0x1234567890123456789012345678901234567890"); - expect(message).toContain("Nonce: abc123"); + expect(message).toContain("Nonce: abc12345def67890"); + expect(message).toContain("Chain ID: 8453"); }); }); @@ -179,4 +182,42 @@ describe("Sign-In-With-X Extension", () => { expect(parsed.signature).toBe(validPayload.signature); }); }); + + describe("Integration - full signing and verification", () => { + it("should sign and verify a message with a real wallet", async () => { + const account = privateKeyToAccount(generatePrivateKey()); + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: "eip155:8453", + statement: "Sign in to access your content", + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, account); + const header = encodeSIWxHeader(payload); + const parsed = parseSIWxHeader(header); + + const validation = await validateSIWxMessage(parsed, "https://api.example.com/resource"); + expect(validation.valid).toBe(true); + + const verification = await verifySIWxSignature(parsed); + expect(verification.valid).toBe(true); + expect(verification.address?.toLowerCase()).toBe(account.address.toLowerCase()); + }); + + it("should reject tampered signature", async () => { + const account = privateKeyToAccount(generatePrivateKey()); + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: "eip155:8453", + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, account); + payload.signature = "0x" + "00".repeat(65); // Invalid signature + + const verification = await verifySIWxSignature(payload); + expect(verification.valid).toBe(false); + }); + }); }); diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml index 9fb342c9d..5e10c89e8 100644 --- a/typescript/pnpm-lock.yaml +++ b/typescript/pnpm-lock.yaml @@ -121,6 +121,9 @@ importers: typescript: specifier: ^5.7.3 version: 5.9.2 + viem: + specifier: ^2.43.5 + version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) vite: specifier: ^6.2.6 version: 6.3.5(@types/node@22.18.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.5)(yaml@2.8.1) @@ -461,19 +464,19 @@ importers: version: 1.2.6 '@solana-program/compute-budget': specifier: ^0.8.0 - version: 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + version: 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) '@solana-program/token': specifier: ^0.5.1 - version: 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + version: 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) '@solana-program/token-2022': specifier: ^0.4.2 - version: 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) + version: 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) '@solana/kit': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-confirmation': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/wallet-standard-features': specifier: ^1.3.0 version: 1.3.0 @@ -767,7 +770,7 @@ importers: version: 1.36.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@solana/kit': specifier: ^5.0.0 - version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) express: specifier: ^4.18.2 version: 4.21.2 @@ -4137,6 +4140,7 @@ packages: '@walletconnect/ethereum-provider@2.21.1': resolution: {integrity: sha512-SSlIG6QEVxClgl1s0LMk4xr2wg4eT3Zn/Hb81IocyqNSGfXpjtawWxKxiC5/9Z95f1INyBD6MctJbL/R1oBwIw==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/events@1.0.1': resolution: {integrity: sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==} @@ -4181,9 +4185,11 @@ packages: '@walletconnect/sign-client@2.21.0': resolution: {integrity: sha512-z7h+PeLa5Au2R591d/8ZlziE0stJvdzP9jNFzFolf2RG/OiXulgFKum8PrIyXy+Rg2q95U9nRVUF9fWcn78yBA==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/sign-client@2.21.1': resolution: {integrity: sha512-QaXzmPsMnKGV6tc4UcdnQVNOz4zyXgarvdIQibJ4L3EmLat73r5ZVl4c0cCOcoaV7rgM9Wbphgu5E/7jNcd3Zg==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/time@1.0.2': resolution: {integrity: sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==} @@ -4196,9 +4202,11 @@ packages: '@walletconnect/universal-provider@2.21.0': resolution: {integrity: sha512-mtUQvewt+X0VBQay/xOJBvxsB3Xsm1lTwFjZ6WUwSOTR1X+FNb71hSApnV5kbsdDIpYPXeQUbGt2se1n5E5UBg==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/universal-provider@2.21.1': resolution: {integrity: sha512-Wjx9G8gUHVMnYfxtasC9poGm8QMiPCpXpbbLFT+iPoQskDDly8BwueWnqKs4Mx2SdIAWAwuXeZ5ojk5qQOxJJg==} + deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/utils@2.21.0': resolution: {integrity: sha512-zfHLiUoBrQ8rP57HTPXW7rQMnYxYI4gT9yTACxVW6LhIFROTF6/ytm5SKNoIvi4a5nX5dfXG4D9XwQUCu8Ilig==} @@ -4245,6 +4253,17 @@ packages: zod: optional: true + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -6127,6 +6146,14 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + ox@0.11.1: + resolution: {integrity: sha512-1l1gOLAqg0S0xiN1dH5nkPna8PucrZgrIJOfS49MLNiMevxu07Iz4ZjuJS9N+xifvT+PsZyIptS7WHM8nC+0+A==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + ox@0.4.4: resolution: {integrity: sha512-oJPEeCDs9iNiPs6J0rTx+Y0KGeCGyCAA3zo94yZhm8G5WpOxrwUtn2Ie/Y8IyARSqqY/j9JTKA3Fc1xs1DvFnw==} peerDependencies: @@ -7239,6 +7266,14 @@ packages: typescript: optional: true + viem@2.43.5: + resolution: {integrity: sha512-QuJpuEMEPM3EreN+vX4mVY68Sci0+zDxozYfbh/WfV+SSy/Gthm74PH8XmitXdty1xY54uTCJ+/Gbbd1IiMPSA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -9861,7 +9896,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 @@ -9890,13 +9925,13 @@ snapshots: dependencies: '@solana/kit': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': + '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana-program/token-2022@0.6.1(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': @@ -9909,9 +9944,9 @@ snapshots: '@solana/kit': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana-program/token@0.9.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: @@ -10292,7 +10327,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -10305,11 +10340,11 @@ snapshots: '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 @@ -10317,26 +10352,27 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/accounts': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/codecs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/errors': 5.0.0(typescript@5.9.2) + '@solana/functional': 5.0.0(typescript@5.9.2) + '@solana/instruction-plans': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/instructions': 5.0.0(typescript@5.9.2) + '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/programs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-parsed-types': 5.0.0(typescript@5.9.2) + '@solana/rpc-spec-types': 5.0.0(typescript@5.9.2) + '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/signers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/sysvars': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transaction-confirmation': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -10633,23 +10669,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) '@solana/functional': 2.3.0(typescript@5.9.2) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) '@solana/subscribable': 2.3.0(typescript@5.9.2) typescript: 5.9.2 - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) + '@solana/errors': 5.0.0(typescript@5.9.2) + '@solana/functional': 5.0.0(typescript@5.9.2) + '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.9.2) + '@solana/subscribable': 5.0.0(typescript@5.9.2) typescript: 5.9.2 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: @@ -10694,7 +10730,7 @@ snapshots: '@solana/subscribable': 5.1.0(typescript@5.9.2) typescript: 5.9.2 - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) @@ -10702,7 +10738,7 @@ snapshots: '@solana/promises': 2.3.0(typescript@5.9.2) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -10712,19 +10748,19 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) + '@solana/errors': 5.0.0(typescript@5.9.2) + '@solana/fast-stable-stringify': 5.0.0(typescript@5.9.2) + '@solana/functional': 5.0.0(typescript@5.9.2) + '@solana/promises': 5.0.0(typescript@5.9.2) + '@solana/rpc-spec-types': 5.0.0(typescript@5.9.2) + '@solana/rpc-subscriptions-api': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-subscriptions-channel-websocket': 5.0.0(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.9.2) + '@solana/rpc-transformers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/subscribable': 5.0.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -11023,7 +11059,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -11031,7 +11067,7 @@ snapshots: '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/promises': 2.3.0(typescript@5.9.2) '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -11040,18 +11076,18 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/codecs-strings': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/errors': 5.0.0(typescript@5.9.2) + '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/promises': 5.0.0(typescript@5.9.2) + '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -12611,6 +12647,11 @@ snapshots: typescript: 5.9.2 zod: 4.1.13 + abitype@1.2.3(typescript@5.9.2)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.2 + zod: 3.25.76 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -14694,6 +14735,21 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + ox@0.11.1(typescript@5.9.2)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.0 + '@noble/ciphers': 1.3.0 + '@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.2)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - zod + ox@0.4.4(typescript@5.9.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.0 @@ -15968,6 +16024,23 @@ snapshots: - utf-8-validate - zod + viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(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.2)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.11.1(typescript@5.9.2)(zod@3.25.76) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + vite-node@3.2.4(@types/node@22.18.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.5)(yaml@2.8.1): dependencies: cac: 6.7.14 From 7fd4a614afa7563654915db5aeb724f68e88ab75 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 11:13:53 -0500 Subject: [PATCH 04/20] address own comments --- .../packages/extensions/src/sign-in-with-x/verify.ts | 6 +----- typescript/packages/extensions/test/sign-in-with-x.test.ts | 7 ------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index d66aec1c7..9a6113f61 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -1,8 +1,5 @@ /** * Signature verification for SIWX extension - * - * Cryptographically verifies SIWX signatures. - * Currently supports EVM (eip191). Extensible for other schemes. */ import { SiweMessage, type VerifyParams, type VerifyOpts } from "siwe"; @@ -38,10 +35,9 @@ export async function verifySIWxSignature( // Parse CAIP-2 chainId (e.g., "eip155:8453" -> 8453) const chainIdMatch = /^eip155:(\d+)$/.exec(payload.chainId); if (!chainIdMatch) { - // TODO: Add support for solana:*, cosmos:*, etc. return { valid: false, - error: `Unsupported chainId namespace: ${payload.chainId}. Currently only eip155:* is supported.`, + error: `Unsupported chainId namespace: ${payload.chainId}. Only eip155:* is supported.`, }; } const numericChainId = parseInt(chainIdMatch[1], 10); diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts index 540dee93e..339104224 100644 --- a/typescript/packages/extensions/test/sign-in-with-x.test.ts +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -4,7 +4,6 @@ import { describe, it, expect } from "vitest"; import { - SIGN_IN_WITH_X, SIWxPayloadSchema, parseSIWxHeader, encodeSIWxHeader, @@ -33,12 +32,6 @@ const validPayload = { }; describe("Sign-In-With-X Extension", () => { - describe("SIGN_IN_WITH_X constant", () => { - it("should export the correct extension identifier", () => { - expect(SIGN_IN_WITH_X).toBe("sign-in-with-x"); - }); - }); - describe("SIWxPayloadSchema", () => { it("should validate a correct payload", () => { const result = SIWxPayloadSchema.safeParse(validPayload); From dec344dd3c2c4a4a169a7970bc6495acd1662ff1 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 11:26:39 -0500 Subject: [PATCH 05/20] update readme, rm TODO.md --- .claude/1_siwx_extension.md | 694 ++++++++++++++++++ X402_NEXTJS_GUIDE.md | 148 ++++ typescript/packages/extensions/README.md | 293 +++++--- .../extensions/src/sign-in-with-x/TODO.md | 146 ---- 4 files changed, 1038 insertions(+), 243 deletions(-) create mode 100644 .claude/1_siwx_extension.md create mode 100644 X402_NEXTJS_GUIDE.md delete mode 100644 typescript/packages/extensions/src/sign-in-with-x/TODO.md diff --git a/.claude/1_siwx_extension.md b/.claude/1_siwx_extension.md new file mode 100644 index 000000000..4061046bc --- /dev/null +++ b/.claude/1_siwx_extension.md @@ -0,0 +1,694 @@ +# Sign-In-With-X (SIWX) Extension Implementation Plan + +## Overview + +Implement the Sign-In-With-X (SIWX) extension for x402 v2, providing CAIP-122 standard wallet-based identity assertions. This extension allows clients to prove control of a wallet that may have previously paid for a resource, enabling servers to grant access without requiring repurchase. + +**Spec References:** +- `typescript/site/CHANGELOG-v2.md` (lines 237-341) +- `typescript/packages/extensions/src/sign-in-with-x/TODO.md` + +## Architecture + +SIWX is a **server-client extension** (no facilitator involvement required per spec line 471). It follows the standard extension pattern with `info` + `schema` structure. + +### Data Flow + +``` +Server Client + | | + |-- 402 PaymentRequired ----------->| + | extensions: { | + | sign-in-with-x: {info, schema}| + | } | + | | + |<-- SIGN-IN-WITH-X header ---------| + | (base64-encoded JSON proof) | + | | + |-- Verify signature locally -------| + |-- 200 Success ------------------->| +``` + +## File Structure + +``` +typescript/packages/extensions/src/sign-in-with-x/ +├── index.ts # Public exports +├── types.ts # Type definitions + SIGN_IN_WITH_X constant +├── schema.ts # JSON Schema builder +├── server.ts # ResourceServerExtension hook +├── declare.ts # declareSIWxExtension() - server declaration helper +├── parse.ts # parseSIWxHeader() - extract from request header +├── validate.ts # validateSIWxMessage() - field/temporal validation +├── verify.ts # verifySIWxSignature() - crypto verification +├── message.ts # createSIWxMessage() - CAIP-122 message construction +├── sign.ts # signSIWxMessage() - client signing +├── encode.ts # encodeSIWxHeader() - base64 encode for header +└── client.ts # createSIWxPayload() - complete client flow +``` + +## Type Definitions (`types.ts`) + +```typescript +// Extension identifier +export const SIGN_IN_WITH_X = 'sign-in-with-x'; + +// Signature schemes per CHANGELOG-v2.md +export type SignatureScheme = + | 'eip191' // personal_sign (default for EVM EOAs) + | 'eip712' // typed data signing + | 'eip1271' // smart contract wallet verification + | 'eip6492' // counterfactual smart wallet verification + | 'siws' // Sign-In-With-Solana + | 'sep10'; // Stellar SEP-10 + +// Server declares in PaymentRequired.extensions +export interface SIWxExtensionInfo { + domain: string; // Server's domain (derived from resourceUri) + uri: string; // Full resource URI + statement?: string; // Human-readable purpose + version: string; // Always "1" per CAIP-122 + chainId: string; // CAIP-2 format: "eip155:8453" + nonce: string; // Cryptographic random (SDK auto-generates) + issuedAt: string; // ISO 8601 (SDK auto-generates) + expirationTime?: string; // Optional expiry (default: +5 min) + notBefore?: string; // Optional validity start + requestId?: string; // Optional correlation ID + resources?: string[]; // Associated resources + signatureScheme?: SignatureScheme; +} + +export interface SIWxExtensionSchema { + $schema: string; + type: 'object'; + properties: Record; + required: string[]; +} + +export interface SIWxExtension { + info: SIWxExtensionInfo; + schema: SIWxExtensionSchema; +} + +// Client proof (sent in SIGN-IN-WITH-X header) +export interface SIWxPayload extends SIWxExtensionInfo { + address: string; // Signing wallet address + signature: string; // Cryptographic signature +} + +// Server declaration options +export interface DeclareSIWxOptions { + resourceUri: string; + statement?: string; + version?: string; // Default: "1" + network: `eip155:${string}` | `solana:${string}` | string; + expirationTime?: string; // Default: auto +5 min + signatureScheme?: SignatureScheme; +} +``` + +## Server-Side Implementation + +### 1. Declaration Helper (`declare.ts`) + +```typescript +import { randomBytes } from 'crypto'; +import type { SIWxExtension, DeclareSIWxOptions } from './types'; +import { SIGN_IN_WITH_X } from './types'; +import { buildSIWxSchema } from './schema'; + +/** + * Server helper to declare SIWX authentication requirement. + * Auto-generates nonce, issuedAt, and derives domain from URI. + */ +export function declareSIWxExtension( + options: DeclareSIWxOptions +): Record { + const url = new URL(options.resourceUri); + const nonce = randomBytes(16).toString('hex'); + const issuedAt = new Date().toISOString(); + const expirationTime = options.expirationTime ?? + new Date(Date.now() + 5 * 60 * 1000).toISOString(); + + const info: SIWxExtensionInfo = { + domain: url.host, + uri: options.resourceUri, + statement: options.statement, + version: options.version ?? '1', + chainId: options.network, + nonce, + issuedAt, + expirationTime, + resources: [options.resourceUri], + ...(options.signatureScheme && { signatureScheme: options.signatureScheme }), + }; + + return { + [SIGN_IN_WITH_X]: { + info, + schema: buildSIWxSchema(), + }, + }; +} +``` + +### 2. JSON Schema Builder (`schema.ts`) + +```typescript +import type { SIWxExtensionSchema } from './types'; + +/** + * Build JSON Schema for SIWX extension validation. + * Per CHANGELOG-v2.md lines 276-292. + */ +export function buildSIWxSchema(): SIWxExtensionSchema { + return { + $schema: 'https://json-schema.org/draft/2020-12/schema', + type: 'object', + properties: { + domain: { type: 'string' }, + address: { type: 'string' }, + statement: { type: 'string' }, + uri: { type: 'string', format: 'uri' }, + version: { type: 'string' }, + chainId: { type: 'string' }, + nonce: { type: 'string' }, + issuedAt: { type: 'string', format: 'date-time' }, + expirationTime: { type: 'string', format: 'date-time' }, + notBefore: { type: 'string', format: 'date-time' }, + requestId: { type: 'string' }, + resources: { type: 'array', items: { type: 'string', format: 'uri' } }, + signature: { type: 'string' }, + }, + required: ['domain', 'address', 'uri', 'version', 'chainId', 'nonce', 'issuedAt', 'signature'], + }; +} +``` + +### 3. Header Parsing (`parse.ts`) + +```typescript +import type { SIWxPayload } from './types'; + +/** + * Parse SIGN-IN-WITH-X header into structured payload. + * Supports both base64-encoded (spec) and raw JSON (backwards compat). + */ +export function parseSIWxHeader(header: string): SIWxPayload { + let jsonStr: string; + + // Try base64 decode first (spec-compliant) + try { + jsonStr = Buffer.from(header, 'base64').toString('utf-8'); + // Verify it's valid JSON + JSON.parse(jsonStr); + } catch { + // Fall back to raw JSON (backwards compatibility) + jsonStr = header; + } + + try { + const payload = JSON.parse(jsonStr) as SIWxPayload; + + // Validate required fields per schema + const required = ['domain', 'address', 'uri', 'version', 'chainId', 'nonce', 'issuedAt', 'signature']; + const missing = required.filter(f => !(f in payload) || !payload[f as keyof SIWxPayload]); + + if (missing.length > 0) { + throw new Error(`Missing required fields: ${missing.join(', ')}`); + } + + return payload; + } catch (error) { + if (error instanceof SyntaxError) { + throw new Error('Invalid SIWX header: not valid JSON or base64'); + } + throw error; + } +} +``` + +### 4. Message Validation (`validate.ts`) + +```typescript +import type { SIWxPayload } from './types'; + +const MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes per spec + +export interface ValidationOptions { + maxAge?: number; + checkNonce?: (nonce: string) => boolean | Promise; +} + +export interface ValidationResult { + valid: boolean; + error?: string; +} + +/** + * Validate SIWX message fields (before signature verification). + * Per CHANGELOG-v2.md validation rules (lines 318-329). + */ +export async function validateSIWxMessage( + message: SIWxPayload, + expectedResourceUri: string, + options: ValidationOptions = {} +): Promise { + const expectedUrl = new URL(expectedResourceUri); + const maxAge = options.maxAge ?? MAX_AGE_MS; + + // 1. Domain binding (spec: "domain field MUST match server's domain") + if (message.domain !== expectedUrl.host) { + return { valid: false, error: `Domain mismatch: expected ${expectedUrl.host}, got ${message.domain}` }; + } + + // 2. URI validation (spec: "uri and resources must refer to base url") + if (!message.uri.startsWith(expectedUrl.origin)) { + return { valid: false, error: `URI mismatch: expected ${expectedUrl.origin}` }; + } + + // 3. issuedAt validation (spec: "MUST be recent, recommended < 5 minutes") + const issuedAt = new Date(message.issuedAt); + if (isNaN(issuedAt.getTime())) { + return { valid: false, error: 'Invalid issuedAt timestamp' }; + } + + const age = Date.now() - issuedAt.getTime(); + if (age > maxAge) { + return { valid: false, error: `Message too old: ${Math.round(age/1000)}s > ${maxAge/1000}s limit` }; + } + if (age < -60000) { // Allow 1 min clock skew + return { valid: false, error: 'issuedAt is in the future' }; + } + + // 4. expirationTime validation (spec: "MUST be in the future") + if (message.expirationTime) { + const expiration = new Date(message.expirationTime); + if (isNaN(expiration.getTime())) { + return { valid: false, error: 'Invalid expirationTime' }; + } + if (expiration < new Date()) { + return { valid: false, error: 'Message expired' }; + } + } + + // 5. notBefore validation (spec: "if present, MUST be in the past") + if (message.notBefore) { + const notBefore = new Date(message.notBefore); + if (new Date() < notBefore) { + return { valid: false, error: 'Message not yet valid (notBefore)' }; + } + } + + // 6. Nonce validation (spec: "MUST be unique per session") + if (options.checkNonce) { + const nonceValid = await options.checkNonce(message.nonce); + if (!nonceValid) { + return { valid: false, error: 'Nonce validation failed (replay detected)' }; + } + } + + return { valid: true }; +} +``` + +### 5. Signature Verification (`verify.ts`) + +```typescript +import { SiweMessage } from 'siwe'; +import type { SIWxPayload, SignatureScheme } from './types'; + +export interface VerifyOptions { + provider?: unknown; // Web3 provider for EIP-1271/6492 + checkSmartWallet?: boolean; +} + +export interface VerifyResult { + valid: boolean; + address?: string; // Recovered/verified address (checksummed) + error?: string; +} + +/** + * Verify SIWX signature cryptographically. + * Currently supports EVM (eip191). Extensible for other schemes. + */ +export async function verifySIWxSignature( + payload: SIWxPayload, + options: VerifyOptions = {} +): Promise { + try { + // Parse CAIP-2 chainId + const chainIdMatch = /^eip155:(\d+)$/.exec(payload.chainId); + if (!chainIdMatch) { + // TODO: Add support for solana:*, etc. + return { valid: false, error: `Unsupported chainId namespace: ${payload.chainId}` }; + } + const numericChainId = parseInt(chainIdMatch[1], 10); + + // Reconstruct SIWE message + const siweMessage = new SiweMessage({ + domain: payload.domain, + address: payload.address, + statement: payload.statement, + uri: payload.uri, + version: payload.version, + chainId: numericChainId, + nonce: payload.nonce, + issuedAt: payload.issuedAt, + expirationTime: payload.expirationTime, + notBefore: payload.notBefore, + requestId: payload.requestId, + resources: payload.resources, + }); + + // Verify signature + const result = await siweMessage.verify({ + signature: payload.signature, + ...(options.checkSmartWallet && options.provider && { provider: options.provider }), + }); + + if (!result.success) { + return { valid: false, error: result.error?.message ?? 'Signature verification failed' }; + } + + return { valid: true, address: siweMessage.address }; + } catch (error) { + return { + valid: false, + error: error instanceof Error ? error.message : 'Verification failed', + }; + } +} +``` + +### 6. ResourceServerExtension Hook (`server.ts`) + +```typescript +import type { ResourceServerExtension } from '@x402/core/types'; +import { SIGN_IN_WITH_X } from './types'; + +/** + * Extension hook for x402ResourceServer.registerExtension(). + * Can enrich declarations based on transport context if needed. + */ +export const siwxResourceServerExtension: ResourceServerExtension = { + key: SIGN_IN_WITH_X, + + enrichDeclaration: (declaration, _transportContext) => { + // Pass through - server explicitly declares SIWX requirements + // Could auto-derive domain from HTTP context in future + return declaration; + }, +}; +``` + +## Client-Side Implementation + +### 7. Message Construction (`message.ts`) + +```typescript +import type { SIWxExtensionInfo } from './types'; + +/** + * Construct CAIP-122 compliant message string for signing. + * Format per https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-122.md + */ +export function createSIWxMessage( + serverInfo: SIWxExtensionInfo, + address: string +): string { + const lines: string[] = []; + + // Header + lines.push(`${serverInfo.domain} wants you to sign in with your ${serverInfo.chainId} account:`); + lines.push(address); + lines.push(''); + + // Statement (optional) + if (serverInfo.statement) { + lines.push(serverInfo.statement); + lines.push(''); + } + + // Required fields + lines.push(`URI: ${serverInfo.uri}`); + lines.push(`Version: ${serverInfo.version}`); + lines.push(`Chain ID: ${serverInfo.chainId}`); + lines.push(`Nonce: ${serverInfo.nonce}`); + lines.push(`Issued At: ${serverInfo.issuedAt}`); + + // Optional fields + if (serverInfo.expirationTime) { + lines.push(`Expiration Time: ${serverInfo.expirationTime}`); + } + if (serverInfo.notBefore) { + lines.push(`Not Before: ${serverInfo.notBefore}`); + } + if (serverInfo.requestId) { + lines.push(`Request ID: ${serverInfo.requestId}`); + } + + // Resources + if (serverInfo.resources && serverInfo.resources.length > 0) { + lines.push('Resources:'); + for (const resource of serverInfo.resources) { + lines.push(`- ${resource}`); + } + } + + return lines.join('\n'); +} +``` + +### 8. Signing Helper (`sign.ts`) + +```typescript +import type { WalletClient } from 'viem'; +import type { PrivateKeyAccount } from 'viem/accounts'; + +export interface SignOptions { + // Future: signatureScheme for EIP-712, etc. +} + +/** + * Sign SIWX message with wallet. + */ +export async function signSIWxMessage( + message: string, + signer: WalletClient | PrivateKeyAccount +): Promise { + // WalletClient (browser wallets) + if ('account' in signer && 'signMessage' in signer) { + const wallet = signer as WalletClient; + if (!wallet.account) { + throw new Error('Wallet account not connected'); + } + return wallet.signMessage({ + account: wallet.account, + message, + }); + } + + // PrivateKeyAccount (server-side) + if ('signMessage' in signer) { + return (signer as PrivateKeyAccount).signMessage({ message }); + } + + throw new Error('Invalid signer type'); +} +``` + +### 9. Header Encoding (`encode.ts`) + +```typescript +import type { SIWxPayload } from './types'; + +/** + * Encode SIWX payload for SIGN-IN-WITH-X header. + * Uses base64 encoding per CHANGELOG-v2.md line 335. + */ +export function encodeSIWxHeader(payload: SIWxPayload): string { + const json = JSON.stringify(payload); + return Buffer.from(json).toString('base64'); +} + +/** + * Encode SIWX payload as raw JSON (for environments without base64 requirement). + */ +export function encodeSIWxHeaderRaw(payload: SIWxPayload): string { + return JSON.stringify(payload); +} +``` + +### 10. Complete Client Flow (`client.ts`) + +```typescript +import type { WalletClient } from 'viem'; +import type { PrivateKeyAccount } from 'viem/accounts'; +import type { SIWxExtensionInfo, SIWxPayload } from './types'; +import { createSIWxMessage } from './message'; +import { signSIWxMessage } from './sign'; + +/** + * Complete client flow: construct message, sign, return payload. + */ +export async function createSIWxPayload( + serverExtension: SIWxExtensionInfo, + signer: WalletClient | PrivateKeyAccount +): Promise { + // Get address from signer + let address: string; + if ('account' in signer && signer.account) { + address = signer.account.address; + } else if ('address' in signer) { + address = (signer as PrivateKeyAccount).address; + } else { + throw new Error('Cannot determine signer address'); + } + + // Construct CAIP-122 message + const message = createSIWxMessage(serverExtension, address); + + // Sign message + const signature = await signSIWxMessage(message, signer); + + // Return complete payload + return { + domain: serverExtension.domain, + address, + statement: serverExtension.statement, + uri: serverExtension.uri, + version: serverExtension.version, + chainId: serverExtension.chainId, + nonce: serverExtension.nonce, + issuedAt: serverExtension.issuedAt, + expirationTime: serverExtension.expirationTime, + notBefore: serverExtension.notBefore, + requestId: serverExtension.requestId, + resources: serverExtension.resources, + signature, + }; +} +``` + +## Public Exports (`index.ts`) + +```typescript +/** + * Sign-In-With-X Extension for x402 v2 + * + * CAIP-122 compliant wallet authentication for payment-protected resources. + */ + +// Extension identifier +export { SIGN_IN_WITH_X } from './types'; + +// Types +export type { + SIWxExtension, + SIWxExtensionInfo, + SIWxExtensionSchema, + SIWxPayload, + DeclareSIWxOptions, + SignatureScheme, +} from './types'; + +// Server exports +export { declareSIWxExtension } from './declare'; +export { parseSIWxHeader } from './parse'; +export { validateSIWxMessage, type ValidationResult, type ValidationOptions } from './validate'; +export { verifySIWxSignature, type VerifyResult, type VerifyOptions } from './verify'; +export { siwxResourceServerExtension } from './server'; +export { buildSIWxSchema } from './schema'; + +// Client exports +export { createSIWxMessage } from './message'; +export { signSIWxMessage, type SignOptions } from './sign'; +export { createSIWxPayload } from './client'; +export { encodeSIWxHeader, encodeSIWxHeaderRaw } from './encode'; +``` + +## Dependencies + +Add to `typescript/packages/extensions/package.json`: + +```json +{ + "dependencies": { + "siwe": "^2.3.0" + } +} +``` + +## Testing Strategy + +```typescript +// test/sign-in-with-x.test.ts + +describe('Sign-In-With-X Extension', () => { + describe('Server Side', () => { + it('declareSIWxExtension generates valid extension with auto-fields'); + it('parseSIWxHeader handles base64 encoded input'); + it('parseSIWxHeader handles raw JSON for backwards compat'); + it('validateSIWxMessage rejects expired messages'); + it('validateSIWxMessage rejects domain mismatch'); + it('validateSIWxMessage rejects old issuedAt (> 5 min)'); + it('verifySIWxSignature validates EIP-191 signatures'); + }); + + describe('Client Side', () => { + it('createSIWxMessage generates CAIP-122 format'); + it('signSIWxMessage signs with PrivateKeyAccount'); + it('encodeSIWxHeader produces valid base64'); + }); + + describe('Integration', () => { + it('server can verify client-generated proof (full flow)'); + }); +}); +``` + +## Implementation Order + +1. **Phase 1**: `types.ts`, `schema.ts` - Foundation +2. **Phase 2**: `declare.ts`, `parse.ts` - Server basics +3. **Phase 3**: `validate.ts`, `verify.ts` - Server validation +4. **Phase 4**: `message.ts`, `sign.ts`, `encode.ts`, `client.ts` - Client side +5. **Phase 5**: `server.ts`, `index.ts` - Integration + exports +6. **Phase 6**: Tests +7. **Phase 7**: Update `tsup.config.ts` for subpath export + +## Package Export Configuration + +Update `typescript/packages/extensions/tsup.config.ts`: + +```typescript +export default defineConfig({ + entry: { + index: 'src/index.ts', + 'bazaar/index': 'src/bazaar/index.ts', + 'sign-in-with-x/index': 'src/sign-in-with-x/index.ts', // Add this + }, + // ... +}); +``` + +Update `package.json` exports: + +```json +{ + "exports": { + "./sign-in-with-x": { + "import": { + "types": "./dist/esm/sign-in-with-x/index.d.mts", + "default": "./dist/esm/sign-in-with-x/index.mjs" + }, + "require": { + "types": "./dist/cjs/sign-in-with-x/index.d.ts", + "default": "./dist/cjs/sign-in-with-x/index.js" + } + } + } +} +``` diff --git a/X402_NEXTJS_GUIDE.md b/X402_NEXTJS_GUIDE.md new file mode 100644 index 000000000..001a53637 --- /dev/null +++ b/X402_NEXTJS_GUIDE.md @@ -0,0 +1,148 @@ +# x402 Payment Protocol Guide for Next.js + +## What is x402? +x402 is a protocol for micropayments on the web. Instead of subscriptions or high minimum payments, you pay small amounts (like $0.001) per API call using cryptocurrency. The payment happens automatically in the HTTP header. + +## Setup + +1. **Install dependencies:** +```bash +npm install x402-fetch @coinbase/cdp-sdk viem +``` + +2. **Generate a wallet and fund it:** +```typescript +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; + +const privateKey = generatePrivateKey(); +const account = privateKeyToAccount(privateKey); +console.log("Address to fund:", account.address); +console.log("Private key:", privateKey); // Store securely +``` + +Fund this address with ETH on Base mainnet for transaction fees. + +3. **Environment variables (.env.local):** +``` +PRIVATE_KEY=0x... # Your generated private key +FIRECRAWL_API_KEY=fc-... # Your Firecrawl API key +``` + +## Next.js API Route Implementation + +**File: `pages/api/search.ts` or `app/api/search/route.ts`** + +```typescript +import { wrapFetchWithPayment, createSigner } from "x402-fetch"; +import type { NextApiRequest, NextApiResponse } from "next"; + +// Initialize once (consider caching the signer) +let signer: any = null; + +async function getSigner() { + if (!signer) { + signer = await createSigner("base", process.env.PRIVATE_KEY!); + } + return signer; +} + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'POST') { + return res.status(405).json({ error: 'Method not allowed' }); + } + + try { + const signer = await getSigner(); + const fetchWithPayment = wrapFetchWithPayment(fetch, signer); + + const response = await fetchWithPayment("https://api.firecrawl.dev/v1/x402/search", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${process.env.FIRECRAWL_API_KEY}`, + }, + body: JSON.stringify(req.body) // Forward client request + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + res.status(200).json(data); + + } catch (error) { + console.error('x402 API call failed:', error); + res.status(500).json({ error: 'Failed to fetch data' }); + } +} +``` + +## How x402 Works + +1. **First request:** Your client makes a normal HTTP request +2. **Payment required:** Server responds with `402 Payment Required` + payment details +3. **Automatic payment:** x402-fetch automatically: + - Creates a blockchain transaction for the required amount + - Signs it with your private key + - Adds payment proof to request headers + - Retries the request +4. **Success:** Server validates payment and returns data + +## Client-Side Usage + +```typescript +// In your React component +const searchData = async (query: string) => { + const response = await fetch('/api/search', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query, + limit: 10, + scrapeOptions: { + formats: ["markdown"], + onlyMainContent: true + } + }) + }); + + return response.json(); +}; +``` + +## Key Concepts + +- **Gasless:** You don't pay blockchain gas fees (handled by facilitator) +- **Automatic:** Payment happens transparently in HTTP headers +- **Micropayments:** Pay exactly what each API call costs ($0.001-$0.01) +- **Networks:** Use "base" for mainnet, "base-sepolia" for testnet +- **Signer:** Manages your private key and creates payment proofs + +## Error Handling + +```typescript +try { + const response = await fetchWithPayment(url, options); + if (!response.ok) { + // Handle HTTP errors (non-payment related) + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + const data = await response.json(); +} catch (error) { + if (error.message?.includes('insufficient funds')) { + // Need to fund your wallet + console.error('Wallet needs ETH for transaction fees'); + } else { + // Other errors (network, API, etc.) + console.error('Request failed:', error); + } +} +``` + +## Security Notes + +- Store private keys in environment variables only +- Never expose private keys in client-side code +- Keep your wallet funded but don't over-fund it +- Consider using a dedicated wallet just for x402 payments \ No newline at end of file diff --git a/typescript/packages/extensions/README.md b/typescript/packages/extensions/README.md index f55373d11..d090596d9 100644 --- a/typescript/packages/extensions/README.md +++ b/typescript/packages/extensions/README.md @@ -1,6 +1,6 @@ # @x402/extensions -x402 Payment Protocol Extensions. This package provides optional extensions that enhance the x402 payment protocol with additional functionality like resource discovery and cataloging. +x402 Payment Protocol Extensions. This package provides optional extensions that enhance the x402 payment protocol with additional functionality. ## Installation @@ -10,10 +10,11 @@ pnpm install @x402/extensions ## Overview -Extensions are optional features that can be added to x402 payment flows. They allow servers to provide additional metadata and enable facilitators to offer enhanced services like resource discovery and cataloging. +Extensions are optional features that can be added to x402 payment flows. They follow a standardized `{ info, schema }` structure and are included in `PaymentRequired.extensions` and `PaymentPayload.extensions`. -Currently, this package includes: -- **Bazaar Discovery Extension**: Enables automatic cataloging and indexing of x402-enabled resources +This package includes: +- **Bazaar Discovery**: Automatic cataloging and indexing of x402-enabled resources +- **Sign-In-With-X (SIWx)**: CAIP-122 wallet authentication for accessing previously purchased resources ## Bazaar Discovery Extension @@ -39,29 +40,29 @@ import { declareDiscoveryExtension } from "@x402/extensions/bazaar"; const resources = { "GET /weather": { - accepts: { - scheme: "exact", - price: "$0.001", - network: "eip155:84532", - payTo: "0xYourAddress" + accepts: { + scheme: "exact", + price: "$0.001", + network: "eip155:84532", + payTo: "0xYourAddress" }, extensions: { ...declareDiscoveryExtension({ input: { city: "San Francisco" }, inputSchema: { - properties: { + properties: { city: { type: "string" }, units: { type: "string", enum: ["celsius", "fahrenheit"] } }, required: ["city"] }, - output: { - example: { - city: "San Francisco", + output: { + example: { + city: "San Francisco", weather: "foggy", temperature: 15, humidity: 85 - } + } }, }), }, @@ -78,15 +79,15 @@ import { declareDiscoveryExtension } from "@x402/extensions/bazaar"; const resources = { "POST /api/translate": { - accepts: { - scheme: "exact", - price: "$0.01", - network: "eip155:84532", - payTo: "0xYourAddress" + accepts: { + scheme: "exact", + price: "$0.01", + network: "eip155:84532", + payTo: "0xYourAddress" }, extensions: { ...declareDiscoveryExtension({ - input: { + input: { text: "Hello, world!", targetLanguage: "es" }, @@ -116,15 +117,15 @@ const resources = { ```typescript const resources = { "PUT /api/user/profile": { - accepts: { - scheme: "exact", - price: "$0.05", - network: "eip155:84532", - payTo: "0xYourAddress" + accepts: { + scheme: "exact", + price: "$0.05", + network: "eip155:84532", + payTo: "0xYourAddress" }, extensions: { ...declareDiscoveryExtension({ - input: { + input: { name: "John Doe", email: "john@example.com", bio: "Software developer" @@ -156,11 +157,11 @@ const resources = { ```typescript const resources = { "DELETE /api/data/:id": { - accepts: { - scheme: "exact", - price: "$0.001", - network: "eip155:84532", - payTo: "0xYourAddress" + accepts: { + scheme: "exact", + price: "$0.001", + network: "eip155:84532", + payTo: "0xYourAddress" }, extensions: { ...declareDiscoveryExtension({ @@ -269,17 +270,17 @@ import { validateDiscoveryExtension, extractDiscoveryInfo } from "@x402/extensio function processPayment(paymentPayload: PaymentPayload, paymentRequirements: PaymentRequirements) { const discovered = extractDiscoveryInfo(paymentPayload, paymentRequirements); - + if (discovered && paymentPayload.extensions?.bazaar) { // Validate the extension schema const validation = validateDiscoveryExtension(paymentPayload.extensions.bazaar); - + if (!validation.valid) { console.warn("Invalid discovery extension:", validation.errors); // Handle invalid extension (log, reject, etc.) return; } - + // Extension is valid, proceed with cataloging catalogResource(discovered); } @@ -300,9 +301,9 @@ const resourceServer = new x402ResourceServer(facilitatorClient) .useExtension(bazaarResourceServerExtension); ``` -## API Reference +### Bazaar API Reference -### `declareDiscoveryExtension(config)` +#### `declareDiscoveryExtension(config)` Creates a discovery extension object for resource servers. @@ -333,7 +334,7 @@ const extension = declareDiscoveryExtension({ // Returns: { bazaar: { info: {...}, schema: {...} } } ``` -### `extractDiscoveryInfo(paymentPayload, paymentRequirements, validate?)` +#### `extractDiscoveryInfo(paymentPayload, paymentRequirements, validate?)` Extracts discovery information from a payment request (for facilitators). @@ -353,88 +354,184 @@ interface DiscoveredResource { } ``` -**Example:** -```typescript -const info = extractDiscoveryInfo(paymentPayload, paymentRequirements); -if (info) { - console.log(info.resourceUrl); // "https://api.example.com/endpoint" - console.log(info.method); // "GET" - console.log(info.discoveryInfo); // { input: {...}, output: {...} } -} -``` - -### `validateDiscoveryExtension(extension)` +#### `validateDiscoveryExtension(extension)` Validates a discovery extension's info against its schema. -**Parameters:** -- `extension`: A discovery extension object - **Returns:** `{ valid: boolean, errors?: string[] }` -**Example:** +#### `validateAndExtract(extension)` + +Validates and extracts discovery info in one step. + +**Returns:** `{ valid: boolean, info?: DiscoveryInfo, errors?: string[] }` + +#### `bazaarResourceServerExtension` + +A server extension that automatically enriches discovery extensions with HTTP method information from the request context. + +#### `BAZAAR` + +The extension identifier constant (`"bazaar"`). + +## Sign-In-With-X Extension + +The Sign-In-With-X extension implements [CAIP-122](https://chainagnostic.org/CAIPs/caip-122) for chain-agnostic wallet authentication. It allows clients to prove control of a wallet that previously paid for a resource, enabling access without repurchase. + +### How It Works + +1. Server returns 402 with `sign-in-with-x` extension containing challenge parameters +2. Client signs the CAIP-122 message with their wallet +3. Client sends signed proof in `SIGN-IN-WITH-X` header +4. Server verifies signature and grants access if wallet has previous payment + +### Server Usage + ```typescript -const result = validateDiscoveryExtension(extension); -if (!result.valid) { - console.error("Validation errors:", result.errors); +import { + declareSIWxExtension, + parseSIWxHeader, + validateSIWxMessage, + verifySIWxSignature, + SIGN_IN_WITH_X, +} from '@x402/extensions/sign-in-with-x'; + +// 1. Declare in PaymentRequired response +const extensions = { + [SIGN_IN_WITH_X]: declareSIWxExtension({ + resourceUri: 'https://api.example.com/data', + network: 'eip155:8453', + statement: 'Sign in to access your purchased content', + }), +}; + +// 2. Verify incoming proof +async function handleRequest(request: Request) { + const header = request.headers.get('SIGN-IN-WITH-X'); + if (!header) return; // No auth provided + + // Parse the header + const payload = parseSIWxHeader(header); + + // Validate message fields (expiry, nonce, domain, etc.) + const validation = await validateSIWxMessage( + payload, + 'https://api.example.com/data' + ); + if (!validation.valid) { + return { error: validation.error }; + } + + // Verify signature and recover address + const verification = await verifySIWxSignature(payload); + if (!verification.valid) { + return { error: verification.error }; + } + + // verification.address is the verified wallet + // Check if this wallet has paid before + const hasPaid = await checkPaymentHistory(verification.address); + if (hasPaid) { + // Grant access without payment + } } ``` -### `validateAndExtract(extension)` +### Client Usage -Validates and extracts discovery info in one step. +```typescript +import { + createSIWxPayload, + encodeSIWxHeader, +} from '@x402/extensions/sign-in-with-x'; -**Parameters:** -- `extension`: A discovery extension object +// 1. Get extension info from 402 response +const paymentRequired = await response.json(); +const serverInfo = paymentRequired.extensions['sign-in-with-x'].info; -**Returns:** `{ valid: boolean, info?: DiscoveryInfo, errors?: string[] }` +// 2. Create signed payload (requires wallet/signer) +const payload = await createSIWxPayload(serverInfo, wallet); -**Example:** -```typescript -const { valid, info, errors } = validateAndExtract(extension); -if (valid && info) { - // Use info -} +// 3. Encode for header +const header = encodeSIWxHeader(payload); + +// 4. Retry request with auth +const response = await fetch(url, { + headers: { 'SIGN-IN-WITH-X': header } +}); ``` -### `bazaarResourceServerExtension` +### SIWx API Reference -A server extension that automatically enriches discovery extensions with HTTP method information from the request context. +#### `declareSIWxExtension(options)` + +Creates the extension object for servers to include in PaymentRequired. -**Usage:** ```typescript -import { bazaarResourceServerExtension } from "@x402/extensions/bazaar"; +declareSIWxExtension({ + resourceUri: string; // Full resource URI (domain derived from this) + network: string; // CAIP-2 network (e.g., "eip155:8453") + statement?: string; // Human-readable purpose + version?: string; // CAIP-122 version (default: "1") + expirationTime?: string; // Optional explicit expiry + signatureScheme?: SignatureScheme; // Hint for client +}) +``` -const resourceServer = new x402ResourceServer(facilitatorClient) - .useExtension(bazaarResourceServerExtension); +#### `parseSIWxHeader(header)` + +Parses a base64-encoded SIGN-IN-WITH-X header into a payload object. + +#### `validateSIWxMessage(payload, resourceUri, options?)` + +Validates message fields (expiry, domain binding, nonce, etc.). + +```typescript +validateSIWxMessage(payload, resourceUri, { + maxAge?: number; // Max age for issuedAt (default: 5 min) + checkNonce?: (nonce) => boolean; // Custom nonce validation +}) +// Returns: { valid: boolean; error?: string } ``` -### `BAZAAR` +#### `verifySIWxSignature(payload, options?)` -The extension identifier constant (`"bazaar"`). +Verifies the cryptographic signature and recovers the signer address. ```typescript -import { BAZAAR } from "@x402/extensions/bazaar"; -// BAZAAR === "bazaar" +verifySIWxSignature(payload, { + provider?: any; // Web3 provider for EIP-1271/6492 + checkSmartWallet?: boolean; // Enable smart wallet verification +}) +// Returns: { valid: boolean; address?: string; error?: string } ``` -## Use Cases +#### `createSIWxPayload(serverInfo, signer)` + +Client helper that creates and signs a complete payload. + +#### `encodeSIWxHeader(payload)` / `encodeSIWxHeaderRaw(payload)` + +Encodes a payload for the SIGN-IN-WITH-X header. -### 1. API Marketplace Discovery -Enable users to discover paid APIs through facilitator catalogs. Servers declare their endpoints, and facilitators index them for easy discovery. +#### `SIGN_IN_WITH_X` -### 2. Developer Tools -Build tools that automatically generate API documentation or client SDKs from discovery metadata. +Extension identifier constant (`"sign-in-with-x"`). -### 3. Resource Cataloging -Facilitators can maintain catalogs of available paid resources, making it easier for users to find services. +### Supported Signature Schemes -### 4. Testing and Validation -Use discovery schemas to validate API requests and responses during development. +| Scheme | Description | +|--------|-------------| +| `eip191` | personal_sign (default for EVM EOAs) | +| `eip712` | Typed data signing | +| `eip1271` | Smart contract wallet verification | +| `eip6492` | Counterfactual smart wallet verification | +| `siws` | Sign-In-With-Solana | +| `sep10` | Stellar SEP-10 | ## Troubleshooting -### Extension Not Being Extracted +### Bazaar Extension Not Being Extracted **Problem:** `extractDiscoveryInfo` returns `null`. @@ -443,7 +540,7 @@ Use discovery schemas to validate API requests and responses during development. - Check that `paymentPayload.extensions.bazaar` exists - Verify you're using x402 v2 (v1 uses a different format in `outputSchema`) -### Schema Validation Fails +### Bazaar Schema Validation Fails **Problem:** `validateDiscoveryExtension` returns `valid: false`. @@ -452,27 +549,29 @@ Use discovery schemas to validate API requests and responses during development. - Check that required fields are marked in `inputSchema.required` - Verify JSON Schema syntax is correct -### Missing Discovery Info +### SIWx Signature Verification Fails -**Problem:** Discovery info is incomplete. +**Problem:** `verifySIWxSignature` returns `valid: false`. **Solutions:** -- Ensure both `input` and `inputSchema` are provided -- For POST/PUT/PATCH, include `bodyType` in the config -- Check that `output.example` is provided if you want output documentation +- Ensure the message was signed with the correct wallet +- Check that the signature scheme matches (eip191 vs eip712) +- For smart wallets, enable `checkSmartWallet` option with a provider -### Method Not Being Detected +### SIWx Message Validation Fails -**Problem:** The HTTP method is missing from discovery info. +**Problem:** `validateSIWxMessage` returns `valid: false`. **Solutions:** -- Use `bazaarResourceServerExtension` which automatically injects the method -- Ensure the route key follows the format `"METHOD /path"` (e.g., `"GET /weather"`) +- Check that `issuedAt` is recent (within `maxAge`, default 5 minutes) +- Verify `expirationTime` hasn't passed +- Ensure `domain` matches the server's domain +- Confirm `uri` matches the resource URI ## Related Resources - [x402 Core Package](../core/README.md) - Core x402 protocol implementation -- [x402 Specification](../../../specs/x402-specification.md) - Full protocol specification +- [CAIP-122 Specification](https://chainagnostic.org/CAIPs/caip-122) - Sign-In-With-X standard ## Version Support diff --git a/typescript/packages/extensions/src/sign-in-with-x/TODO.md b/typescript/packages/extensions/src/sign-in-with-x/TODO.md deleted file mode 100644 index d7c902418..000000000 --- a/typescript/packages/extensions/src/sign-in-with-x/TODO.md +++ /dev/null @@ -1,146 +0,0 @@ -# Sign-In-With-X Extension TODO - -## Overview - -Implement the Sign-In-With-X (SIWx) extension for the x402 protocol, providing CAIP-122 standard wallet-based identity assertions. This extension allows clients to prove control of a wallet that may have previously paid for a resource, enabling servers to grant access without requiring repurchase. - -## Core Exports Required - -### Server-Side Exports - -```typescript -// Extension declaration helper for servers -export function declareSIWxExtension(options: { - resourceUri: string; // Full URI of the resource (domain derived from this) - statement?: string; - version?: string; // Defaults to "1" - network: `${string}:${string}`; // e.g., "eip155:8453" - expirationTime?: string; // Auto-set to +5 minutes if not provided - signatureScheme?: 'eip191' | 'eip712' | 'eip1271' | 'eip6492' | 'siws' | 'sep10'; -}): SIWxExtension; - -// Header parsing for servers -export function parseSIWxHeader(header: string): SIWxPayload; - -// Message validation -export function validateSIWxMessage( - message: SIWxPayload, - expectedResourceUri: string, // Validates domain is derived correctly from URI - options?: { - maxAge?: number; // Maximum age of issuedAt (default: 5 minutes) - checkNonce?: (nonce: string) => boolean; // Custom nonce validation - } -): { valid: boolean; error?: string }; - -// Signature verification -export function verifySIWxSignature( - message: SIWxPayload, - signature: string, - options?: { - provider?: any; // Web3 provider for on-chain verification - checkSmartWallet?: boolean; // Enable EIP-1271/6492 verification - } -): Promise<{ valid: boolean; address?: string; error?: string }>; -``` - -### Client-Side Exports - -```typescript -// Header encoding for clients -export function encodeSIWxHeader(payload: SIWxPayload): string; - -// Message construction helper -export function createSIWxMessage( - serverInfo: SIWxExtensionInfo, - address: string -): string; // Returns CAIP-122 formatted message string - -// Signature creation -export function signSIWxMessage( - message: string, - signer: any, // Wallet/signer interface - options?: { - signatureScheme?: 'eip191' | 'eip712' | 'eip1271' | 'eip6492'; - } -): Promise; - -// Complete flow helper -export function createSIWxPayload( - serverExtension: SIWxExtension, - signer: any -): Promise; -``` - -### Type Definitions - -```typescript -export interface SIWxExtensionInfo { - domain: string; // Auto-derived from resourceUri (without https://) - uri: string; // Same as resourceUri - statement?: string; - version: string; - chainId: string; // Derived from network (e.g., "eip155:8453") - nonce: string; // Auto-generated by SDK - issuedAt: string; // Auto-generated by SDK - expirationTime?: string; - notBefore?: string; - requestId?: string; - resources: string[]; // Auto-generated as [resourceUri] - signatureScheme?: string; -} - -export interface SIWxExtension { - info: SIWxExtensionInfo; - schema: object; // JSON Schema for validation -} - -export interface SIWxPayload { - domain: string; - address: string; - statement?: string; - uri: string; - version: string; - chainId: string; - nonce: string; - issuedAt: string; - expirationTime?: string; - notBefore?: string; - requestId?: string; - resources?: string[]; - signature: string; -} -``` - -## Implementation Requirements - -### Auto-Generated Fields - -The SDK will automatically generate the following fields from the provided options: -- **domain**: Extracted from `resourceUri` by removing the protocol (https://) -- **uri**: Same as the provided `resourceUri` -- **chainId**: Same as the provided `network` parameter -- **nonce**: Cryptographically secure random string -- **issuedAt**: Current timestamp in ISO 8601 format -- **resources**: Array containing the `resourceUri` - -### CAIP-122 Message Format - -Implement proper CAIP-122 message construction: -``` -{domain} wants you to sign in with your {chainId} account: -{address} - -{statement} - -URI: {uri} -Version: {version} -Chain ID: {chainId} -Nonce: {nonce} -Issued At: {issuedAt} -Expiration Time: {expirationTime} -Not Before: {notBefore} -Request ID: {requestId} -Resources: -- {resource1} -- {resource2} -``` From 9957c11ec871cdccf4573ec7c5224e6b5422a349 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 12:36:10 -0500 Subject: [PATCH 06/20] add SIWS --- typescript/packages/extensions/package.json | 3 +- .../extensions/src/sign-in-with-x/index.ts | 12 + .../extensions/src/sign-in-with-x/message.ts | 43 +++- .../extensions/src/sign-in-with-x/solana.ts | 230 +++++++++++++++++ .../extensions/src/sign-in-with-x/types.ts | 18 +- .../extensions/src/sign-in-with-x/verify.ts | 184 +++++++++++--- .../extensions/test/sign-in-with-x.test.ts | 238 ++++++++++++++++++ typescript/pnpm-lock.yaml | 153 +++++------ 8 files changed, 757 insertions(+), 124 deletions(-) create mode 100644 typescript/packages/extensions/src/sign-in-with-x/solana.ts diff --git a/typescript/packages/extensions/package.json b/typescript/packages/extensions/package.json index f53a94535..c3b6cffeb 100644 --- a/typescript/packages/extensions/package.json +++ b/typescript/packages/extensions/package.json @@ -38,7 +38,6 @@ "tsup": "^8.4.0", "tsx": "^4.19.2", "typescript": "^5.7.3", - "viem": "^2.43.5", "vite": "^6.2.6", "vite-tsconfig-paths": "^5.1.4", "vitest": "^3.0.5" @@ -47,6 +46,8 @@ "@x402/core": "workspace:*", "ajv": "^8.17.1", "siwe": "^2.3.2", + "tweetnacl": "^1.0.3", + "viem": "^2.43.5", "zod": "^3.24.2" }, "exports": { diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 746cc6aad..007804815 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -70,6 +70,9 @@ // Extension identifier and schema export { SIGN_IN_WITH_X, SIWxPayloadSchema } from "./types"; +// Solana network constants +export { SOLANA_MAINNET, SOLANA_DEVNET, SOLANA_TESTNET } from "./types"; + // Types export type { SIWxExtension, @@ -96,3 +99,12 @@ export { createSIWxMessage } from "./message"; export { signSIWxMessage, type SIWxSigner } from "./sign"; export { createSIWxPayload } from "./client"; export { encodeSIWxHeader, encodeSIWxHeaderRaw } from "./encode"; + +// Solana utilities +export { + formatSIWSMessage, + verifySolanaSignature, + decodeBase58, + encodeBase58, + extractSolanaNetwork, +} from "./solana"; diff --git a/typescript/packages/extensions/src/sign-in-with-x/message.ts b/typescript/packages/extensions/src/sign-in-with-x/message.ts index 1698025da..8e2c369f0 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/message.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/message.ts @@ -2,33 +2,60 @@ * CAIP-122 message construction for SIWX extension * * Constructs the canonical message string for signing. - * Uses siwe library for EIP-4361 compliant message format. + * Routes to chain-specific formatters based on chainId namespace. */ import { SiweMessage } from "siwe"; +import { formatSIWSMessage } from "./solana"; import type { SIWxExtensionInfo } from "./types"; /** - * Construct EIP-4361 compliant message string for signing. + * Construct CAIP-122 compliant message string for signing. * - * Uses the siwe library to ensure message format matches verification. + * Routes to the appropriate chain-specific message formatter based on the + * chainId namespace prefix: + * - `eip155:*` → SIWE (EIP-4361) format via siwe library + * - `solana:*` → SIWS format * * @param serverInfo - Server-provided extension info * @param address - Client wallet address * @returns Message string ready for signing + * @throws Error if chainId namespace is not supported * * @example * ```typescript - * const serverInfo = paymentRequired.extensions['sign-in-with-x'].info; - * const message = createSIWxMessage(serverInfo, wallet.address); - * const signature = await wallet.signMessage({ message }); + * // EVM (Ethereum, Base, etc.) + * const evmMessage = createSIWxMessage(serverInfo, "0x1234..."); + * + * // Solana + * const solMessage = createSIWxMessage(serverInfo, "BSmWDg..."); * ``` */ export function createSIWxMessage(serverInfo: SIWxExtensionInfo, address: string): string { - // Parse CAIP-2 chainId (e.g., "eip155:8453" -> 8453) + // Route by chain namespace + if (serverInfo.chainId.startsWith("eip155:")) { + return createEVMMessage(serverInfo, address); + } + + if (serverInfo.chainId.startsWith("solana:")) { + return formatSIWSMessage(serverInfo, address); + } + + throw new Error( + `Unsupported chain namespace: ${serverInfo.chainId}. ` + + `Supported: eip155:* (EVM), solana:* (Solana)`, + ); +} + +/** + * Create EIP-4361 (SIWE) compliant message for EVM chains. + * + * Uses the siwe library to ensure message format matches verification. + */ +function createEVMMessage(serverInfo: SIWxExtensionInfo, address: string): string { const chainIdMatch = /^eip155:(\d+)$/.exec(serverInfo.chainId); if (!chainIdMatch) { - throw new Error(`Unsupported chainId format: ${serverInfo.chainId}. Expected eip155:`); + throw new Error(`Invalid EVM chainId format: ${serverInfo.chainId}. Expected eip155:`); } const numericChainId = parseInt(chainIdMatch[1], 10); diff --git a/typescript/packages/extensions/src/sign-in-with-x/solana.ts b/typescript/packages/extensions/src/sign-in-with-x/solana.ts new file mode 100644 index 000000000..100a7e29c --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/solana.ts @@ -0,0 +1,230 @@ +/** + * Solana Sign-In-With-X (SIWS) support + * + * Implements CAIP-122 compliant message format and Ed25519 signature verification + * for Solana wallets. + */ + +import nacl from "tweetnacl"; +import type { SIWxExtensionInfo } from "./types"; + +/** + * Known Solana network genesis hashes mapped to human-readable names. + * Used for Chain ID display in SIWS messages. + */ +const SOLANA_NETWORKS: Record = { + "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "mainnet", + EtWTRABZaYq6iMfeYKouRu166VU2xqa1: "devnet", + "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": "testnet", +}; + +/** + * Extract network name from CAIP-2 Solana chainId. + * + * @param chainId - CAIP-2 format chain ID (e.g., "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp") + * @returns Human-readable network name or the reference if unknown + * + * @example + * ```typescript + * extractSolanaNetwork("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp") // "mainnet" + * extractSolanaNetwork("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") // "devnet" + * extractSolanaNetwork("solana:custom123") // "custom123" + * ``` + */ +export function extractSolanaNetwork(chainId: string): string { + const [, reference] = chainId.split(":"); + return SOLANA_NETWORKS[reference] ?? reference; +} + +/** + * Format SIWS message following CAIP-122 ABNF specification. + * + * The message format is identical to SIWE (EIP-4361) but uses "Solana account" + * instead of "Ethereum account" in the header line. + * + * @param info - Server-provided extension info + * @param address - Client's Solana wallet address (Base58 encoded public key) + * @returns Message string ready for signing + * + * @example + * ```typescript + * const message = formatSIWSMessage(serverInfo, "BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); + * // Returns: + * // "api.example.com wants you to sign in with your Solana account: + * // BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW + * // + * // Sign in to access your content + * // + * // URI: https://api.example.com/data + * // Version: 1 + * // Chain ID: mainnet + * // Nonce: abc123 + * // Issued At: 2024-01-01T00:00:00.000Z" + * ``` + */ +export function formatSIWSMessage(info: SIWxExtensionInfo, address: string): string { + const lines: string[] = [ + `${info.domain} wants you to sign in with your Solana account:`, + address, + "", + ]; + + // Statement (optional, with blank line after) + if (info.statement) { + lines.push(info.statement, ""); + } + + // Required fields + lines.push( + `URI: ${info.uri}`, + `Version: ${info.version}`, + `Chain ID: ${extractSolanaNetwork(info.chainId)}`, + `Nonce: ${info.nonce}`, + `Issued At: ${info.issuedAt}`, + ); + + // Optional fields + if (info.expirationTime) { + lines.push(`Expiration Time: ${info.expirationTime}`); + } + if (info.notBefore) { + lines.push(`Not Before: ${info.notBefore}`); + } + if (info.requestId) { + lines.push(`Request ID: ${info.requestId}`); + } + + // Resources (optional) + if (info.resources && info.resources.length > 0) { + lines.push("Resources:"); + for (const resource of info.resources) { + lines.push(`- ${resource}`); + } + } + + return lines.join("\n"); +} + +/** + * Verify Ed25519 signature for SIWS. + * + * @param message - The SIWS message that was signed + * @param signature - Ed25519 signature bytes + * @param publicKey - Solana public key bytes (32 bytes) + * @returns true if signature is valid + * + * @example + * ```typescript + * const messageBytes = new TextEncoder().encode(message); + * const valid = verifySolanaSignature(message, signatureBytes, publicKeyBytes); + * ``` + */ +export function verifySolanaSignature( + message: string, + signature: Uint8Array, + publicKey: Uint8Array, +): boolean { + const messageBytes = new TextEncoder().encode(message); + return nacl.sign.detached.verify(messageBytes, signature, publicKey); +} + +/** + * Decode Base58 string to bytes. + * + * Solana uses Base58 encoding (Bitcoin alphabet) for addresses and signatures. + * This is a minimal implementation to avoid adding bs58 as a dependency. + * + * @param encoded - Base58 encoded string + * @returns Decoded bytes + * @throws Error if string contains invalid Base58 characters + * + * @example + * ```typescript + * const publicKeyBytes = decodeBase58("BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); + * // Returns Uint8Array of 32 bytes + * ``` + */ +export function decodeBase58(encoded: string): Uint8Array { + const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + // Count leading zeros (Base58 '1' = 0x00 byte) + let leadingZeros = 0; + for (const char of encoded) { + if (char === "1") { + leadingZeros++; + } else { + break; + } + } + + // Decode the rest + const bytes: number[] = []; + for (const char of encoded) { + let carry = ALPHABET.indexOf(char); + if (carry < 0) { + throw new Error(`Invalid Base58 character: ${char}`); + } + + for (let i = 0; i < bytes.length; i++) { + carry += bytes[i] * 58; + bytes[i] = carry & 0xff; + carry >>= 8; + } + + while (carry > 0) { + bytes.push(carry & 0xff); + carry >>= 8; + } + } + + // Reverse and prepend leading zeros + bytes.reverse(); + const result = new Uint8Array(leadingZeros + bytes.length); + result.set(bytes, leadingZeros); + + return result; +} + +/** + * Encode bytes to Base58 string. + * + * @param bytes - Bytes to encode + * @returns Base58 encoded string + */ +export function encodeBase58(bytes: Uint8Array): string { + const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + + // Count leading zeros + let leadingZeros = 0; + for (const byte of bytes) { + if (byte === 0) { + leadingZeros++; + } else { + break; + } + } + + // Encode the rest + const digits: number[] = []; + for (const byte of bytes) { + let carry = byte; + for (let i = 0; i < digits.length; i++) { + carry += digits[i] << 8; + digits[i] = carry % 58; + carry = Math.floor(carry / 58); + } + + while (carry > 0) { + digits.push(carry % 58); + carry = Math.floor(carry / 58); + } + } + + // Convert to string with leading '1's for zeros + let result = "1".repeat(leadingZeros); + for (let i = digits.length - 1; i >= 0; i--) { + result += ALPHABET[digits[i]]; + } + + return result; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/types.ts b/typescript/packages/extensions/src/sign-in-with-x/types.ts index 6197a2759..31a02dd63 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/types.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/types.ts @@ -117,6 +117,14 @@ export const SIWxPayloadSchema = z.object({ */ export type SIWxPayload = z.infer; +/** + * Common Solana network CAIP-2 identifiers. + * Uses genesis hash as the chain reference per CAIP-30. + */ +export const SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; +export const SOLANA_DEVNET = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"; +export const SOLANA_TESTNET = "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z"; + /** * Options for declaring SIWX extension on server */ @@ -127,8 +135,14 @@ export interface DeclareSIWxOptions { statement?: string; /** CAIP-122 version (default: "1") */ version?: string; - /** CAIP-2 network identifier (e.g., "eip155:8453") */ - network: string; + /** + * CAIP-2 network identifier. + * - EVM: "eip155:8453" (Base), "eip155:1" (Ethereum mainnet) + * - Solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" (mainnet) + * + * Use SOLANA_MAINNET, SOLANA_DEVNET, or SOLANA_TESTNET constants for Solana. + */ + network: `eip155:${string}` | `solana:${string}` | (string & {}); /** Optional explicit expiration time */ expirationTime?: string; /** Signature scheme hint */ diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index 9a6113f61..a944b4f22 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -1,18 +1,26 @@ /** * Signature verification for SIWX extension + * + * Routes to chain-specific verification based on chainId namespace: + * - EVM (eip155:*): Uses viem's verifyMessage with EIP-6492 smart wallet support + * - Solana (solana:*): Uses Ed25519 signature verification via tweetnacl */ -import { SiweMessage, type VerifyParams, type VerifyOpts } from "siwe"; +import { verifyMessage } from "viem"; +import { SiweMessage } from "siwe"; +import { formatSIWSMessage, verifySolanaSignature, decodeBase58 } from "./solana"; import type { SIWxPayload, SIWxVerifyResult, SIWxVerifyOptions } from "./types"; /** * Verify SIWX signature cryptographically. * - * Reconstructs the SIWE message from payload fields and verifies - * the signature matches the claimed address. + * Routes to the appropriate chain-specific verification based on the + * chainId namespace prefix: + * - `eip155:*` → EVM verification with EIP-6492 smart wallet support + * - `solana:*` → Ed25519 signature verification * * @param payload - The SIWX payload containing signature - * @param options - Verification options + * @param options - Verification options (primarily for EVM) * @returns Verification result with recovered address if valid * * @example @@ -32,64 +40,160 @@ export async function verifySIWxSignature( options: SIWxVerifyOptions = {}, ): Promise { try { - // Parse CAIP-2 chainId (e.g., "eip155:8453" -> 8453) - const chainIdMatch = /^eip155:(\d+)$/.exec(payload.chainId); - if (!chainIdMatch) { + // Route by chain namespace + if (payload.chainId.startsWith("eip155:")) { + return verifyEVMSignature(payload, options); + } + + if (payload.chainId.startsWith("solana:")) { + return verifySolanaPayload(payload); + } + + return { + valid: false, + error: `Unsupported chain namespace: ${payload.chainId}. Supported: eip155:* (EVM), solana:* (Solana)`, + }; + } catch (error) { + return { + valid: false, + error: error instanceof Error ? error.message : "Verification failed", + }; + } +} + +/** + * Verify EVM signature with EIP-6492 smart wallet support. + * + * Uses viem's verifyMessage which automatically handles: + * - EOA signatures (standard ECDSA) + * - EIP-1271 (deployed smart contract wallets) + * - EIP-6492 (counterfactual/pre-deploy smart wallets) + */ +async function verifyEVMSignature( + payload: SIWxPayload, + _options: SIWxVerifyOptions, +): Promise { + const chainIdMatch = /^eip155:(\d+)$/.exec(payload.chainId); + if (!chainIdMatch) { + return { + valid: false, + error: `Invalid EVM chainId format: ${payload.chainId}. Expected eip155:`, + }; + } + const numericChainId = parseInt(chainIdMatch[1], 10); + + // Reconstruct SIWE message for verification + const siweMessage = new SiweMessage({ + domain: payload.domain, + address: payload.address, + statement: payload.statement, + uri: payload.uri, + version: payload.version, + chainId: numericChainId, + nonce: payload.nonce, + issuedAt: payload.issuedAt, + expirationTime: payload.expirationTime, + notBefore: payload.notBefore, + requestId: payload.requestId, + resources: payload.resources, + }); + + const message = siweMessage.prepareMessage(); + + // Use viem's verifyMessage for EIP-6492 smart wallet support + // This handles EOA, EIP-1271, and EIP-6492 signatures automatically + try { + const valid = await verifyMessage({ + address: payload.address as `0x${string}`, + message, + signature: payload.signature as `0x${string}`, + }); + + if (!valid) { return { valid: false, - error: `Unsupported chainId namespace: ${payload.chainId}. Only eip155:* is supported.`, + error: "Signature verification failed", }; } - const numericChainId = parseInt(chainIdMatch[1], 10); - // Reconstruct SIWE message for verification - const siweMessage = new SiweMessage({ - domain: payload.domain, + return { + valid: true, address: payload.address, - statement: payload.statement, + }; + } catch (error) { + return { + valid: false, + error: error instanceof Error ? error.message : "Signature verification failed", + }; + } +} + +/** + * Verify Solana Ed25519 signature. + * + * Reconstructs the SIWS message and verifies using tweetnacl. + */ +function verifySolanaPayload(payload: SIWxPayload): SIWxVerifyResult { + // Reconstruct SIWS message + const message = formatSIWSMessage( + { + domain: payload.domain, uri: payload.uri, + statement: payload.statement, version: payload.version, - chainId: numericChainId, + chainId: payload.chainId, nonce: payload.nonce, issuedAt: payload.issuedAt, expirationTime: payload.expirationTime, notBefore: payload.notBefore, requestId: payload.requestId, resources: payload.resources, - }); - - // Verify signature - const verifyParams: VerifyParams = { - signature: payload.signature, - }; + }, + payload.address, + ); - // Add provider for smart wallet verification if enabled - const verifyOpts: VerifyOpts | undefined = - options.checkSmartWallet && options.provider - ? { provider: options.provider } - : undefined; + // Decode Base58 signature and public key + let signature: Uint8Array; + let publicKey: Uint8Array; - const result = await siweMessage.verify(verifyParams, verifyOpts); + try { + signature = decodeBase58(payload.signature); + publicKey = decodeBase58(payload.address); + } catch (error) { + return { + valid: false, + error: `Invalid Base58 encoding: ${error instanceof Error ? error.message : "decode failed"}`, + }; + } - if (!result.success) { - // SiweError type - extract error details - const errorMessage = result.error - ? String(result.error) - : "Signature verification failed"; - return { - valid: false, - error: errorMessage, - }; - } + // Validate signature length (Ed25519 signatures are 64 bytes) + if (signature.length !== 64) { + return { + valid: false, + error: `Invalid signature length: expected 64 bytes, got ${signature.length}`, + }; + } + // Validate public key length (Ed25519 public keys are 32 bytes) + if (publicKey.length !== 32) { return { - valid: true, - address: siweMessage.address, + valid: false, + error: `Invalid public key length: expected 32 bytes, got ${publicKey.length}`, }; - } catch (error) { + } + + // Verify Ed25519 signature + const valid = verifySolanaSignature(message, signature, publicKey); + + if (!valid) { return { valid: false, - error: error instanceof Error ? error.message : "Verification failed", + error: "Solana signature verification failed", }; } + + return { + valid: true, + address: payload.address, + }; } diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts index 339104224..9d9507436 100644 --- a/typescript/packages/extensions/test/sign-in-with-x.test.ts +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -13,9 +13,17 @@ import { createSIWxMessage, createSIWxPayload, verifySIWxSignature, + SOLANA_MAINNET, + SOLANA_DEVNET, + formatSIWSMessage, + decodeBase58, + encodeBase58, + extractSolanaNetwork, + verifySolanaSignature, } from "../src/sign-in-with-x/index"; import { safeBase64Encode } from "@x402/core/utils"; import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; +import nacl from "tweetnacl"; const validPayload = { domain: "api.example.com", @@ -213,4 +221,234 @@ describe("Sign-In-With-X Extension", () => { expect(verification.valid).toBe(false); }); }); + + describe("Solana constants", () => { + it("should export Solana network constants", () => { + expect(SOLANA_MAINNET).toBe("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"); + expect(SOLANA_DEVNET).toBe("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"); + }); + }); + + describe("Base58 encoding/decoding", () => { + it("should roundtrip encode/decode", () => { + const original = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + const encoded = encodeBase58(original); + const decoded = decodeBase58(encoded); + expect(decoded).toEqual(original); + }); + + it("should handle leading zeros", () => { + const withLeadingZeros = new Uint8Array([0, 0, 1, 2, 3]); + const encoded = encodeBase58(withLeadingZeros); + const decoded = decodeBase58(encoded); + expect(decoded).toEqual(withLeadingZeros); + }); + + it("should decode known Solana addresses", () => { + // This is a valid 32-byte Solana public key + const address = "11111111111111111111111111111111"; + const decoded = decodeBase58(address); + expect(decoded.length).toBe(32); + }); + + it("should throw on invalid Base58 characters", () => { + expect(() => decodeBase58("invalid0OIl")).toThrow("Invalid Base58 character"); + }); + }); + + describe("extractSolanaNetwork", () => { + it("should extract mainnet", () => { + expect(extractSolanaNetwork(SOLANA_MAINNET)).toBe("mainnet"); + }); + + it("should extract devnet", () => { + expect(extractSolanaNetwork(SOLANA_DEVNET)).toBe("devnet"); + }); + + it("should return reference for unknown networks", () => { + expect(extractSolanaNetwork("solana:customnetwork123")).toBe("customnetwork123"); + }); + }); + + describe("formatSIWSMessage", () => { + it("should format SIWS message correctly", () => { + const info = { + domain: "api.example.com", + uri: "https://api.example.com/data", + statement: "Sign in to access", + version: "1", + chainId: SOLANA_MAINNET, + nonce: "abc123", + issuedAt: "2024-01-01T00:00:00.000Z", + resources: ["https://api.example.com/data"], + }; + + const message = formatSIWSMessage(info, "BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); + + expect(message).toContain("wants you to sign in with your Solana account:"); + expect(message).toContain("BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); + expect(message).toContain("Chain ID: mainnet"); + expect(message).toContain("Nonce: abc123"); + expect(message).toContain("Sign in to access"); + }); + + it("should handle message without statement", () => { + const info = { + domain: "api.example.com", + uri: "https://api.example.com", + version: "1", + chainId: SOLANA_DEVNET, + nonce: "xyz789", + issuedAt: "2024-01-01T00:00:00.000Z", + }; + + const message = formatSIWSMessage(info, "TestAddress123"); + + expect(message).toContain("wants you to sign in with your Solana account:"); + expect(message).toContain("Chain ID: devnet"); + expect(message).not.toContain("Sign in to access"); + }); + }); + + describe("createSIWxMessage - chain routing", () => { + it("should route EVM chains to SIWE format", () => { + const info = { + domain: "api.example.com", + uri: "https://api.example.com", + version: "1", + chainId: "eip155:1", + nonce: "abc12345678", + issuedAt: "2024-01-01T00:00:00.000Z", + }; + + const message = createSIWxMessage(info, "0x1234567890123456789012345678901234567890"); + + expect(message).toContain("wants you to sign in with your Ethereum account:"); + expect(message).toContain("Chain ID: 1"); + }); + + it("should route Solana chains to SIWS format", () => { + const info = { + domain: "api.example.com", + uri: "https://api.example.com", + version: "1", + chainId: SOLANA_MAINNET, + nonce: "abc12345678", + issuedAt: "2024-01-01T00:00:00.000Z", + }; + + const message = createSIWxMessage(info, "BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); + + expect(message).toContain("wants you to sign in with your Solana account:"); + expect(message).toContain("Chain ID: mainnet"); + }); + + it("should throw for unsupported chain namespaces", () => { + const info = { + domain: "api.example.com", + uri: "https://api.example.com", + version: "1", + chainId: "cosmos:cosmoshub-4", + nonce: "abc12345678", + issuedAt: "2024-01-01T00:00:00.000Z", + }; + + expect(() => createSIWxMessage(info, "cosmos1...")).toThrow("Unsupported chain namespace"); + }); + }); + + describe("Solana signature verification", () => { + it("should verify valid Ed25519 signature", () => { + // Generate a test keypair + const keypair = nacl.sign.keyPair(); + const message = "Test message for signing"; + const messageBytes = new TextEncoder().encode(message); + const signature = nacl.sign.detached(messageBytes, keypair.secretKey); + + const valid = verifySolanaSignature(message, signature, keypair.publicKey); + expect(valid).toBe(true); + }); + + it("should reject invalid signature", () => { + const keypair = nacl.sign.keyPair(); + const message = "Test message"; + const wrongSignature = new Uint8Array(64).fill(0); + + const valid = verifySolanaSignature(message, wrongSignature, keypair.publicKey); + expect(valid).toBe(false); + }); + + it("should reject signature from different key", () => { + const keypair1 = nacl.sign.keyPair(); + const keypair2 = nacl.sign.keyPair(); + const message = "Test message"; + const messageBytes = new TextEncoder().encode(message); + const signature = nacl.sign.detached(messageBytes, keypair1.secretKey); + + // Verify with different public key + const valid = verifySolanaSignature(message, signature, keypair2.publicKey); + expect(valid).toBe(false); + }); + }); + + describe("verifySIWxSignature - chain routing", () => { + it("should reject unsupported chain namespace", async () => { + const payload = { + ...validPayload, + chainId: "cosmos:cosmoshub-4", + }; + + const result = await verifySIWxSignature(payload); + expect(result.valid).toBe(false); + expect(result.error).toContain("Unsupported chain namespace"); + }); + + it("should verify Solana signatures", async () => { + // Generate Solana keypair + const keypair = nacl.sign.keyPair(); + const address = encodeBase58(keypair.publicKey); + + const serverInfo = { + domain: "api.example.com", + uri: "https://api.example.com/data", + version: "1", + chainId: SOLANA_MAINNET, + nonce: "test123", + issuedAt: new Date().toISOString(), + }; + + // Create and sign SIWS message + const message = formatSIWSMessage(serverInfo, address); + const messageBytes = new TextEncoder().encode(message); + const signatureBytes = nacl.sign.detached(messageBytes, keypair.secretKey); + const signature = encodeBase58(signatureBytes); + + const payload = { + ...serverInfo, + address, + signature, + }; + + const result = await verifySIWxSignature(payload); + expect(result.valid).toBe(true); + expect(result.address).toBe(address); + }); + + it("should reject invalid Solana signature length", async () => { + const payload = { + domain: "api.example.com", + uri: "https://api.example.com", + version: "1", + chainId: SOLANA_MAINNET, + nonce: "test123", + issuedAt: new Date().toISOString(), + address: encodeBase58(new Uint8Array(32).fill(1)), // Valid 32-byte key + signature: encodeBase58(new Uint8Array(32).fill(0)), // Invalid 32-byte sig (should be 64) + }; + + const result = await verifySIWxSignature(payload); + expect(result.valid).toBe(false); + expect(result.error).toContain("Invalid signature length"); + }); + }); }); diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml index 5e10c89e8..b31318790 100644 --- a/typescript/pnpm-lock.yaml +++ b/typescript/pnpm-lock.yaml @@ -81,6 +81,12 @@ importers: siwe: specifier: ^2.3.2 version: 2.3.2(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + tweetnacl: + specifier: ^1.0.3 + version: 1.0.3 + viem: + specifier: ^2.43.5 + version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zod: specifier: ^3.24.2 version: 3.25.76 @@ -121,9 +127,6 @@ importers: typescript: specifier: ^5.7.3 version: 5.9.2 - viem: - specifier: ^2.43.5 - version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) vite: specifier: ^6.2.6 version: 6.3.5(@types/node@22.18.0)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.5)(yaml@2.8.1) @@ -464,19 +467,19 @@ importers: version: 1.2.6 '@solana-program/compute-budget': specifier: ^0.8.0 - version: 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + version: 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) '@solana-program/token': specifier: ^0.5.1 - version: 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + version: 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) '@solana-program/token-2022': specifier: ^0.4.2 - version: 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) + version: 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) '@solana/kit': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-confirmation': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/wallet-standard-features': specifier: ^1.3.0 version: 1.3.0 @@ -770,7 +773,7 @@ importers: version: 1.36.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@solana/kit': specifier: ^5.0.0 - version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) express: specifier: ^4.18.2 version: 4.21.2 @@ -7039,6 +7042,9 @@ packages: resolution: {integrity: sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w==} hasBin: true + tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -9925,13 +9931,13 @@ snapshots: dependencies: '@solana/kit': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': + '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana-program/token-2022@0.6.1(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': @@ -9944,9 +9950,9 @@ snapshots: '@solana/kit': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana-program/token@0.9.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: @@ -10327,7 +10333,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -10340,11 +10346,11 @@ snapshots: '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 @@ -10352,27 +10358,26 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/accounts': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 5.0.0(typescript@5.9.2) - '@solana/functional': 5.0.0(typescript@5.9.2) - '@solana/instruction-plans': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/instructions': 5.0.0(typescript@5.9.2) - '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/programs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 5.0.0(typescript@5.9.2) - '@solana/rpc-spec-types': 5.0.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/signers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/sysvars': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/errors': 2.3.0(typescript@5.9.2) + '@solana/functional': 2.3.0(typescript@5.9.2) + '@solana/instructions': 2.3.0(typescript@5.9.2) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -10669,23 +10674,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) '@solana/functional': 2.3.0(typescript@5.9.2) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) '@solana/subscribable': 2.3.0(typescript@5.9.2) typescript: 5.9.2 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 5.0.0(typescript@5.9.2) - '@solana/functional': 5.0.0(typescript@5.9.2) - '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.9.2) - '@solana/subscribable': 5.0.0(typescript@5.9.2) + '@solana/errors': 2.3.0(typescript@5.9.2) + '@solana/functional': 2.3.0(typescript@5.9.2) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) + '@solana/subscribable': 2.3.0(typescript@5.9.2) typescript: 5.9.2 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: @@ -10730,7 +10735,7 @@ snapshots: '@solana/subscribable': 5.1.0(typescript@5.9.2) typescript: 5.9.2 - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) @@ -10738,7 +10743,7 @@ snapshots: '@solana/promises': 2.3.0(typescript@5.9.2) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -10748,19 +10753,19 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 5.0.0(typescript@5.9.2) - '@solana/fast-stable-stringify': 5.0.0(typescript@5.9.2) - '@solana/functional': 5.0.0(typescript@5.9.2) - '@solana/promises': 5.0.0(typescript@5.9.2) - '@solana/rpc-spec-types': 5.0.0(typescript@5.9.2) - '@solana/rpc-subscriptions-api': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 5.0.0(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.9.2) - '@solana/rpc-transformers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/subscribable': 5.0.0(typescript@5.9.2) + '@solana/errors': 2.3.0(typescript@5.9.2) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) + '@solana/functional': 2.3.0(typescript@5.9.2) + '@solana/promises': 2.3.0(typescript@5.9.2) + '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) + '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/subscribable': 2.3.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -11059,7 +11064,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -11067,7 +11072,7 @@ snapshots: '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/promises': 2.3.0(typescript@5.9.2) '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -11076,18 +11081,18 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-strings': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 5.0.0(typescript@5.9.2) - '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/promises': 5.0.0(typescript@5.9.2) - '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/errors': 2.3.0(typescript@5.9.2) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/promises': 2.3.0(typescript@5.9.2) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -15761,6 +15766,8 @@ snapshots: turbo-windows-64: 2.5.6 turbo-windows-arm64: 2.5.6 + tweetnacl@1.0.3: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 From e5836049636bd45ab2f33cd2cd3e9a68c28f222d Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 13:30:32 -0500 Subject: [PATCH 07/20] cleanup --- X402_NEXTJS_GUIDE.md | 148 ------------------ typescript/packages/extensions/package.json | 1 + .../extensions/src/sign-in-with-x/parse.ts | 29 +--- .../extensions/src/sign-in-with-x/solana.ts | 77 +-------- .../extensions/test/sign-in-with-x.test.ts | 23 ++- typescript/pnpm-lock.yaml | 142 +++++++++-------- 6 files changed, 96 insertions(+), 324 deletions(-) delete mode 100644 X402_NEXTJS_GUIDE.md diff --git a/X402_NEXTJS_GUIDE.md b/X402_NEXTJS_GUIDE.md deleted file mode 100644 index 001a53637..000000000 --- a/X402_NEXTJS_GUIDE.md +++ /dev/null @@ -1,148 +0,0 @@ -# x402 Payment Protocol Guide for Next.js - -## What is x402? -x402 is a protocol for micropayments on the web. Instead of subscriptions or high minimum payments, you pay small amounts (like $0.001) per API call using cryptocurrency. The payment happens automatically in the HTTP header. - -## Setup - -1. **Install dependencies:** -```bash -npm install x402-fetch @coinbase/cdp-sdk viem -``` - -2. **Generate a wallet and fund it:** -```typescript -import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; - -const privateKey = generatePrivateKey(); -const account = privateKeyToAccount(privateKey); -console.log("Address to fund:", account.address); -console.log("Private key:", privateKey); // Store securely -``` - -Fund this address with ETH on Base mainnet for transaction fees. - -3. **Environment variables (.env.local):** -``` -PRIVATE_KEY=0x... # Your generated private key -FIRECRAWL_API_KEY=fc-... # Your Firecrawl API key -``` - -## Next.js API Route Implementation - -**File: `pages/api/search.ts` or `app/api/search/route.ts`** - -```typescript -import { wrapFetchWithPayment, createSigner } from "x402-fetch"; -import type { NextApiRequest, NextApiResponse } from "next"; - -// Initialize once (consider caching the signer) -let signer: any = null; - -async function getSigner() { - if (!signer) { - signer = await createSigner("base", process.env.PRIVATE_KEY!); - } - return signer; -} - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method !== 'POST') { - return res.status(405).json({ error: 'Method not allowed' }); - } - - try { - const signer = await getSigner(); - const fetchWithPayment = wrapFetchWithPayment(fetch, signer); - - const response = await fetchWithPayment("https://api.firecrawl.dev/v1/x402/search", { - method: "POST", - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${process.env.FIRECRAWL_API_KEY}`, - }, - body: JSON.stringify(req.body) // Forward client request - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const data = await response.json(); - res.status(200).json(data); - - } catch (error) { - console.error('x402 API call failed:', error); - res.status(500).json({ error: 'Failed to fetch data' }); - } -} -``` - -## How x402 Works - -1. **First request:** Your client makes a normal HTTP request -2. **Payment required:** Server responds with `402 Payment Required` + payment details -3. **Automatic payment:** x402-fetch automatically: - - Creates a blockchain transaction for the required amount - - Signs it with your private key - - Adds payment proof to request headers - - Retries the request -4. **Success:** Server validates payment and returns data - -## Client-Side Usage - -```typescript -// In your React component -const searchData = async (query: string) => { - const response = await fetch('/api/search', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - query, - limit: 10, - scrapeOptions: { - formats: ["markdown"], - onlyMainContent: true - } - }) - }); - - return response.json(); -}; -``` - -## Key Concepts - -- **Gasless:** You don't pay blockchain gas fees (handled by facilitator) -- **Automatic:** Payment happens transparently in HTTP headers -- **Micropayments:** Pay exactly what each API call costs ($0.001-$0.01) -- **Networks:** Use "base" for mainnet, "base-sepolia" for testnet -- **Signer:** Manages your private key and creates payment proofs - -## Error Handling - -```typescript -try { - const response = await fetchWithPayment(url, options); - if (!response.ok) { - // Handle HTTP errors (non-payment related) - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - const data = await response.json(); -} catch (error) { - if (error.message?.includes('insufficient funds')) { - // Need to fund your wallet - console.error('Wallet needs ETH for transaction fees'); - } else { - // Other errors (network, API, etc.) - console.error('Request failed:', error); - } -} -``` - -## Security Notes - -- Store private keys in environment variables only -- Never expose private keys in client-side code -- Keep your wallet funded but don't over-fund it -- Consider using a dedicated wallet just for x402 payments \ No newline at end of file diff --git a/typescript/packages/extensions/package.json b/typescript/packages/extensions/package.json index c3b6cffeb..a4277f861 100644 --- a/typescript/packages/extensions/package.json +++ b/typescript/packages/extensions/package.json @@ -43,6 +43,7 @@ "vitest": "^3.0.5" }, "dependencies": { + "@scure/base": "^1.2.6", "@x402/core": "workspace:*", "ajv": "^8.17.1", "siwe": "^2.3.2", diff --git a/typescript/packages/extensions/src/sign-in-with-x/parse.ts b/typescript/packages/extensions/src/sign-in-with-x/parse.ts index 39e3d2460..dcdf86d7d 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/parse.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/parse.ts @@ -2,7 +2,7 @@ * Header parsing for SIWX extension * * Parses the SIGN-IN-WITH-X header from client requests. - * Supports both base64-encoded (spec) and raw JSON (backwards compat). + * Requires base64-encoded JSON per x402 v2 spec. */ import { Base64EncodedRegex, safeBase64Decode } from "@x402/core/utils"; @@ -11,11 +11,9 @@ import { SIWxPayloadSchema, type SIWxPayload } from "./types"; /** * Parse SIGN-IN-WITH-X header into structured payload. * - * Supports both: - * - Base64-encoded JSON (spec-compliant, per CHANGELOG-v2.md line 335) - * - Raw JSON (backwards compatibility) + * Expects base64-encoded JSON per x402 v2 spec (CHANGELOG-v2.md line 335). * - * @param header - The SIGN-IN-WITH-X header value + * @param header - The SIGN-IN-WITH-X header value (base64-encoded JSON) * @returns Parsed SIWX payload * @throws Error if header is invalid or missing required fields * @@ -29,33 +27,22 @@ import { SIWxPayloadSchema, type SIWxPayload } from "./types"; * ``` */ export function parseSIWxHeader(header: string): SIWxPayload { - let jsonStr: string; - - // Try base64 decode first (spec-compliant) - if (Base64EncodedRegex.test(header)) { - try { - jsonStr = safeBase64Decode(header); - } catch { - // If base64 decode fails, treat as raw JSON - jsonStr = header; - } - } else { - // Fall back to raw JSON (backwards compatibility) - jsonStr = header; + if (!Base64EncodedRegex.test(header)) { + throw new Error("Invalid SIWX header: not valid base64"); } - // Parse JSON + const jsonStr = safeBase64Decode(header); + let rawPayload: unknown; try { rawPayload = JSON.parse(jsonStr); } catch (error) { if (error instanceof SyntaxError) { - throw new Error("Invalid SIWX header: not valid JSON or base64-encoded JSON"); + throw new Error("Invalid SIWX header: not valid JSON"); } throw error; } - // Validate with zod schema const parsed = SIWxPayloadSchema.safeParse(rawPayload); if (!parsed.success) { diff --git a/typescript/packages/extensions/src/sign-in-with-x/solana.ts b/typescript/packages/extensions/src/sign-in-with-x/solana.ts index 100a7e29c..b433f8a81 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/solana.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/solana.ts @@ -5,6 +5,7 @@ * for Solana wallets. */ +import { base58 } from "@scure/base"; import nacl from "tweetnacl"; import type { SIWxExtensionInfo } from "./types"; @@ -132,7 +133,6 @@ export function verifySolanaSignature( * Decode Base58 string to bytes. * * Solana uses Base58 encoding (Bitcoin alphabet) for addresses and signatures. - * This is a minimal implementation to avoid adding bs58 as a dependency. * * @param encoded - Base58 encoded string * @returns Decoded bytes @@ -145,44 +145,7 @@ export function verifySolanaSignature( * ``` */ export function decodeBase58(encoded: string): Uint8Array { - const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - - // Count leading zeros (Base58 '1' = 0x00 byte) - let leadingZeros = 0; - for (const char of encoded) { - if (char === "1") { - leadingZeros++; - } else { - break; - } - } - - // Decode the rest - const bytes: number[] = []; - for (const char of encoded) { - let carry = ALPHABET.indexOf(char); - if (carry < 0) { - throw new Error(`Invalid Base58 character: ${char}`); - } - - for (let i = 0; i < bytes.length; i++) { - carry += bytes[i] * 58; - bytes[i] = carry & 0xff; - carry >>= 8; - } - - while (carry > 0) { - bytes.push(carry & 0xff); - carry >>= 8; - } - } - - // Reverse and prepend leading zeros - bytes.reverse(); - const result = new Uint8Array(leadingZeros + bytes.length); - result.set(bytes, leadingZeros); - - return result; + return base58.decode(encoded); } /** @@ -192,39 +155,5 @@ export function decodeBase58(encoded: string): Uint8Array { * @returns Base58 encoded string */ export function encodeBase58(bytes: Uint8Array): string { - const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - - // Count leading zeros - let leadingZeros = 0; - for (const byte of bytes) { - if (byte === 0) { - leadingZeros++; - } else { - break; - } - } - - // Encode the rest - const digits: number[] = []; - for (const byte of bytes) { - let carry = byte; - for (let i = 0; i < digits.length; i++) { - carry += digits[i] << 8; - digits[i] = carry % 58; - carry = Math.floor(carry / 58); - } - - while (carry > 0) { - digits.push(carry % 58); - carry = Math.floor(carry / 58); - } - } - - // Convert to string with leading '1's for zeros - let result = "1".repeat(leadingZeros); - for (let i = digits.length - 1; i >= 0; i--) { - result += ALPHABET[digits[i]]; - } - - return result; + return base58.encode(bytes); } diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts index 9d9507436..2b8ff6843 100644 --- a/typescript/packages/extensions/test/sign-in-with-x.test.ts +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -77,29 +77,28 @@ describe("Sign-In-With-X Extension", () => { expect(parsed.signature).toBe(validPayload.signature); }); - it("should parse raw JSON header (backwards compatibility)", () => { - const raw = JSON.stringify(validPayload); - const parsed = parseSIWxHeader(raw); - expect(parsed.domain).toBe(validPayload.domain); - expect(parsed.signature).toBe(validPayload.signature); + it("should throw on invalid base64", () => { + expect(() => parseSIWxHeader("not-valid-base64!@#")).toThrow("not valid base64"); }); - it("should throw on invalid JSON", () => { - expect(() => parseSIWxHeader("not-valid-json")).toThrow("Invalid SIWX header"); + it("should throw on invalid JSON in base64", () => { + const invalidJson = safeBase64Encode("not valid json"); + expect(() => parseSIWxHeader(invalidJson)).toThrow("not valid JSON"); }); it("should throw on missing required fields", () => { - const incomplete = JSON.stringify({ domain: "example.com" }); + const incomplete = safeBase64Encode(JSON.stringify({ domain: "example.com" })); expect(() => parseSIWxHeader(incomplete)).toThrow("Invalid SIWX header"); }); }); describe("encodeSIWxHeader", () => { - it("should encode payload as base64", () => { + it("should encode payload as base64 and round-trip correctly", () => { const encoded = encodeSIWxHeader(validPayload); - expect(() => Buffer.from(encoded, "base64")).not.toThrow(); - const decoded = JSON.parse(Buffer.from(encoded, "base64").toString("utf-8")); + const decoded = parseSIWxHeader(encoded); expect(decoded.domain).toBe(validPayload.domain); + expect(decoded.address).toBe(validPayload.address); + expect(decoded.signature).toBe(validPayload.signature); }); }); @@ -252,7 +251,7 @@ describe("Sign-In-With-X Extension", () => { }); it("should throw on invalid Base58 characters", () => { - expect(() => decodeBase58("invalid0OIl")).toThrow("Invalid Base58 character"); + expect(() => decodeBase58("invalid0OIl")).toThrow("Unknown letter"); }); }); diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml index b31318790..04c458b70 100644 --- a/typescript/pnpm-lock.yaml +++ b/typescript/pnpm-lock.yaml @@ -72,6 +72,9 @@ importers: packages/extensions: dependencies: + '@scure/base': + specifier: ^1.2.6 + version: 1.2.6 '@x402/core': specifier: workspace:* version: link:../core @@ -467,19 +470,19 @@ importers: version: 1.2.6 '@solana-program/compute-budget': specifier: ^0.8.0 - version: 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + version: 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) '@solana-program/token': specifier: ^0.5.1 - version: 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + version: 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) '@solana-program/token-2022': specifier: ^0.4.2 - version: 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) + version: 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) '@solana/kit': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-confirmation': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/wallet-standard-features': specifier: ^1.3.0 version: 1.3.0 @@ -773,7 +776,7 @@ importers: version: 1.36.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@solana/kit': specifier: ^5.0.0 - version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) express: specifier: ^4.18.2 version: 4.21.2 @@ -9931,13 +9934,13 @@ snapshots: dependencies: '@solana/kit': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': + '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana-program/token-2022@0.6.1(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': @@ -9950,9 +9953,9 @@ snapshots: '@solana/kit': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana-program/token@0.9.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: @@ -10333,7 +10336,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -10346,11 +10349,11 @@ snapshots: '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 @@ -10358,26 +10361,27 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/accounts': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/codecs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/errors': 5.0.0(typescript@5.9.2) + '@solana/functional': 5.0.0(typescript@5.9.2) + '@solana/instruction-plans': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/instructions': 5.0.0(typescript@5.9.2) + '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/programs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-parsed-types': 5.0.0(typescript@5.9.2) + '@solana/rpc-spec-types': 5.0.0(typescript@5.9.2) + '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/signers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/sysvars': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transaction-confirmation': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -10674,23 +10678,23 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) '@solana/functional': 2.3.0(typescript@5.9.2) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) '@solana/subscribable': 2.3.0(typescript@5.9.2) typescript: 5.9.2 - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) + '@solana/errors': 5.0.0(typescript@5.9.2) + '@solana/functional': 5.0.0(typescript@5.9.2) + '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.9.2) + '@solana/subscribable': 5.0.0(typescript@5.9.2) typescript: 5.9.2 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: @@ -10735,7 +10739,7 @@ snapshots: '@solana/subscribable': 5.1.0(typescript@5.9.2) typescript: 5.9.2 - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) @@ -10743,7 +10747,7 @@ snapshots: '@solana/promises': 2.3.0(typescript@5.9.2) '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -10753,19 +10757,19 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) + '@solana/errors': 5.0.0(typescript@5.9.2) + '@solana/fast-stable-stringify': 5.0.0(typescript@5.9.2) + '@solana/functional': 5.0.0(typescript@5.9.2) + '@solana/promises': 5.0.0(typescript@5.9.2) + '@solana/rpc-spec-types': 5.0.0(typescript@5.9.2) + '@solana/rpc-subscriptions-api': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-subscriptions-channel-websocket': 5.0.0(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.9.2) + '@solana/rpc-transformers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/subscribable': 5.0.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -11064,7 +11068,7 @@ snapshots: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -11072,7 +11076,7 @@ snapshots: '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/promises': 2.3.0(typescript@5.9.2) '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -11081,18 +11085,18 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/transaction-confirmation@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/codecs-strings': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/errors': 5.0.0(typescript@5.9.2) + '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/promises': 5.0.0(typescript@5.9.2) + '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder From 71a7e718ed8ae8ac68f7fdf30aeaa43e256c2327 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 13:35:54 -0500 Subject: [PATCH 08/20] further cleanup --- typescript/packages/extensions/README.md | 4 ++-- .../extensions/src/sign-in-with-x/encode.ts | 14 -------------- .../extensions/src/sign-in-with-x/index.ts | 2 +- .../extensions/src/sign-in-with-x/verify.ts | 4 ++-- .../extensions/test/sign-in-with-x.test.ts | 9 --------- 5 files changed, 5 insertions(+), 28 deletions(-) diff --git a/typescript/packages/extensions/README.md b/typescript/packages/extensions/README.md index d090596d9..e0b5c6507 100644 --- a/typescript/packages/extensions/README.md +++ b/typescript/packages/extensions/README.md @@ -510,9 +510,9 @@ verifySIWxSignature(payload, { Client helper that creates and signs a complete payload. -#### `encodeSIWxHeader(payload)` / `encodeSIWxHeaderRaw(payload)` +#### `encodeSIWxHeader(payload)` -Encodes a payload for the SIGN-IN-WITH-X header. +Encodes a payload as base64 for the SIGN-IN-WITH-X header. #### `SIGN_IN_WITH_X` diff --git a/typescript/packages/extensions/src/sign-in-with-x/encode.ts b/typescript/packages/extensions/src/sign-in-with-x/encode.ts index 3d0eb2cb8..dd59142cb 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/encode.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/encode.ts @@ -29,17 +29,3 @@ import type { SIWxPayload } from "./types"; export function encodeSIWxHeader(payload: SIWxPayload): string { return safeBase64Encode(JSON.stringify(payload)); } - -/** - * Encode SIWX payload as raw JSON. - * - * For environments where base64 encoding is not required. - * Note: This is NOT spec-compliant but may be useful for debugging - * or legacy compatibility. - * - * @param payload - Complete SIWX payload with signature - * @returns JSON string (not base64 encoded) - */ -export function encodeSIWxHeaderRaw(payload: SIWxPayload): string { - return JSON.stringify(payload); -} diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 007804815..578e5d063 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -98,7 +98,7 @@ export { buildSIWxSchema } from "./schema"; export { createSIWxMessage } from "./message"; export { signSIWxMessage, type SIWxSigner } from "./sign"; export { createSIWxPayload } from "./client"; -export { encodeSIWxHeader, encodeSIWxHeaderRaw } from "./encode"; +export { encodeSIWxHeader } from "./encode"; // Solana utilities export { diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index a944b4f22..6ead5af82 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -42,7 +42,7 @@ export async function verifySIWxSignature( try { // Route by chain namespace if (payload.chainId.startsWith("eip155:")) { - return verifyEVMSignature(payload, options); + return verifyEVMPayload(payload, options); } if (payload.chainId.startsWith("solana:")) { @@ -69,7 +69,7 @@ export async function verifySIWxSignature( * - EIP-1271 (deployed smart contract wallets) * - EIP-6492 (counterfactual/pre-deploy smart wallets) */ -async function verifyEVMSignature( +async function verifyEVMPayload( payload: SIWxPayload, _options: SIWxVerifyOptions, ): Promise { diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts index 2b8ff6843..85bb77807 100644 --- a/typescript/packages/extensions/test/sign-in-with-x.test.ts +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -7,7 +7,6 @@ import { SIWxPayloadSchema, parseSIWxHeader, encodeSIWxHeader, - encodeSIWxHeaderRaw, declareSIWxExtension, validateSIWxMessage, createSIWxMessage, @@ -102,14 +101,6 @@ describe("Sign-In-With-X Extension", () => { }); }); - describe("encodeSIWxHeaderRaw", () => { - it("should encode payload as raw JSON", () => { - const encoded = encodeSIWxHeaderRaw(validPayload); - const decoded = JSON.parse(encoded); - expect(decoded.domain).toBe(validPayload.domain); - }); - }); - describe("declareSIWxExtension", () => { it("should create extension with auto-generated fields", () => { const result = declareSIWxExtension({ From 06a4839a179ad37cf248e2b32c55934751432f00 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 14:16:21 -0500 Subject: [PATCH 09/20] split code path btwn evm and solana --- .../extensions/src/sign-in-with-x/client.ts | 56 +++--- .../extensions/src/sign-in-with-x/evm.ts | 108 ++++++++++ .../extensions/src/sign-in-with-x/index.ts | 35 ++-- .../extensions/src/sign-in-with-x/message.ts | 34 +--- .../extensions/src/sign-in-with-x/sign.ts | 94 +++++---- .../extensions/src/sign-in-with-x/types.ts | 30 +-- .../extensions/src/sign-in-with-x/verify.ts | 65 ++---- .../extensions/test/sign-in-with-x.test.ts | 188 ++++++++++++++++++ 8 files changed, 439 insertions(+), 171 deletions(-) create mode 100644 typescript/packages/extensions/src/sign-in-with-x/evm.ts diff --git a/typescript/packages/extensions/src/sign-in-with-x/client.ts b/typescript/packages/extensions/src/sign-in-with-x/client.ts index f93a27eb5..99efab01e 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/client.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/client.ts @@ -2,62 +2,56 @@ * Complete client flow for SIWX extension * * Combines message construction, signing, and payload creation. + * Supports both EVM and Solana wallets. */ import type { SIWxExtensionInfo, SIWxPayload } from "./types"; -import type { SIWxSigner } from "./sign"; +import type { SIWxSigner, EVMSigner, SolanaSigner } from "./sign"; +import { + getEVMAddress, + getSolanaAddress, + signEVMMessage, + signSolanaMessage, +} from "./sign"; import { createSIWxMessage } from "./message"; -import { signSIWxMessage } from "./sign"; /** * Create a complete SIWX payload from server extension info. * - * This function: - * 1. Extracts the wallet address from the signer - * 2. Constructs the CAIP-122 message - * 3. Signs the message - * 4. Returns the complete payload ready for encoding + * Routes to EVM or Solana signing based on the chainId prefix: + * - `eip155:*` → EVM signing + * - `solana:*` → Solana signing * * @param serverExtension - Server-provided extension info from PaymentRequired - * @param signer - Wallet or account that can sign messages + * @param signer - Wallet that can sign messages (EVMSigner or SolanaSigner) * @returns Complete SIWX payload with signature * * @example * ```typescript - * // Get extension info from 402 response - * const serverInfo = paymentRequired.extensions['sign-in-with-x'].info; + * // EVM wallet + * const payload = await createSIWxPayload(serverInfo, evmWallet); * - * // Create signed payload - * const payload = await createSIWxPayload(serverInfo, wallet); - * - * // Encode for header - * const header = encodeSIWxHeader(payload); - * - * // Send authenticated request - * fetch(url, { headers: { 'SIGN-IN-WITH-X': header } }); + * // Solana wallet + * const payload = await createSIWxPayload(serverInfo, solanaSigner); * ``` */ export async function createSIWxPayload( serverExtension: SIWxExtensionInfo, signer: SIWxSigner, ): Promise { - // Get address from signer - let address: string; - if (signer.account?.address) { - address = signer.account.address; - } else if (signer.address) { - address = signer.address; - } else { - throw new Error("Cannot determine signer address: no account or address property found"); - } + const isSolana = serverExtension.chainId.startsWith("solana:"); + + // Get address and sign based on chain type + const address = isSolana + ? getSolanaAddress(signer as SolanaSigner) + : getEVMAddress(signer as EVMSigner); - // Construct CAIP-122 message const message = createSIWxMessage(serverExtension, address); - // Sign message - const signature = await signSIWxMessage(message, signer); + const signature = isSolana + ? await signSolanaMessage(message, signer as SolanaSigner) + : await signEVMMessage(message, signer as EVMSigner); - // Return complete payload return { domain: serverExtension.domain, address, diff --git a/typescript/packages/extensions/src/sign-in-with-x/evm.ts b/typescript/packages/extensions/src/sign-in-with-x/evm.ts new file mode 100644 index 000000000..32fe004f7 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/evm.ts @@ -0,0 +1,108 @@ +/** + * EVM Sign-In-With-Ethereum (SIWE) support + * + * Implements EIP-4361 compliant message format and signature verification + * for EVM chains (Ethereum, Base, Polygon, etc.) + */ + +import { verifyMessage } from "viem"; +import { SiweMessage } from "siwe"; +import type { SIWxExtensionInfo } from "./types"; + +/** + * Extract numeric chain ID from CAIP-2 EVM chainId. + * + * @param chainId - CAIP-2 format chain ID (e.g., "eip155:8453") + * @returns Numeric chain ID (e.g., 8453) + * @throws Error if chainId format is invalid + * + * @example + * ```typescript + * extractEVMChainId("eip155:1") // 1 (Ethereum mainnet) + * extractEVMChainId("eip155:8453") // 8453 (Base) + * extractEVMChainId("eip155:137") // 137 (Polygon) + * ``` + */ +export function extractEVMChainId(chainId: string): number { + const match = /^eip155:(\d+)$/.exec(chainId); + if (!match) { + throw new Error(`Invalid EVM chainId format: ${chainId}. Expected eip155:`); + } + return parseInt(match[1], 10); +} + +/** + * Format SIWE message following EIP-4361 specification. + * + * Uses the siwe library to ensure message format matches verification. + * + * @param info - Server-provided extension info + * @param address - Client's EVM wallet address (0x-prefixed) + * @returns Message string ready for signing + * + * @example + * ```typescript + * const message = formatSIWEMessage(serverInfo, "0x1234...abcd"); + * // Returns EIP-4361 formatted message: + * // "api.example.com wants you to sign in with your Ethereum account: + * // 0x1234...abcd + * // + * // Sign in to access your content + * // + * // URI: https://api.example.com/data + * // Version: 1 + * // Chain ID: 8453 + * // Nonce: abc123 + * // Issued At: 2024-01-01T00:00:00.000Z" + * ``` + */ +export function formatSIWEMessage(info: SIWxExtensionInfo, address: string): string { + const numericChainId = extractEVMChainId(info.chainId); + + const siweMessage = new SiweMessage({ + domain: info.domain, + address, + statement: info.statement, + uri: info.uri, + version: info.version, + chainId: numericChainId, + nonce: info.nonce, + issuedAt: info.issuedAt, + expirationTime: info.expirationTime, + notBefore: info.notBefore, + requestId: info.requestId, + resources: info.resources, + }); + + return siweMessage.prepareMessage(); +} + +/** + * Verify EVM signature with EIP-6492 smart wallet support. + * + * Uses viem's verifyMessage which automatically handles: + * - EOA signatures (standard ECDSA via EIP-191) + * - EIP-1271 (deployed smart contract wallets) + * - EIP-6492 (counterfactual/pre-deploy smart wallets) + * + * @param message - The SIWE message that was signed + * @param address - The claimed signer address + * @param signature - The signature to verify + * @returns true if signature is valid + * + * @example + * ```typescript + * const valid = await verifyEVMSignature(message, "0x1234...", "0xsig..."); + * ``` + */ +export async function verifyEVMSignature( + message: string, + address: string, + signature: string, +): Promise { + return verifyMessage({ + address: address as `0x${string}`, + message, + signature: signature as `0x${string}`, + }); +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 578e5d063..3c35543ec 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -67,11 +67,8 @@ * @packageDocumentation */ -// Extension identifier and schema -export { SIGN_IN_WITH_X, SIWxPayloadSchema } from "./types"; - -// Solana network constants -export { SOLANA_MAINNET, SOLANA_DEVNET, SOLANA_TESTNET } from "./types"; +// Constants +export { SIGN_IN_WITH_X, SOLANA_MAINNET, SOLANA_DEVNET, SOLANA_TESTNET, SIWxPayloadSchema } from "./types"; // Types export type { @@ -84,27 +81,31 @@ export type { SIWxValidationResult, SIWxValidationOptions, SIWxVerifyResult, - SIWxVerifyOptions, } from "./types"; -// Server exports +// Server export { declareSIWxExtension } from "./declare"; export { parseSIWxHeader } from "./parse"; export { validateSIWxMessage } from "./validate"; export { verifySIWxSignature } from "./verify"; export { buildSIWxSchema } from "./schema"; -// Client exports +// Client export { createSIWxMessage } from "./message"; -export { signSIWxMessage, type SIWxSigner } from "./sign"; export { createSIWxPayload } from "./client"; export { encodeSIWxHeader } from "./encode"; - -// Solana utilities export { - formatSIWSMessage, - verifySolanaSignature, - decodeBase58, - encodeBase58, - extractSolanaNetwork, -} from "./solana"; + getEVMAddress, + getSolanaAddress, + signEVMMessage, + signSolanaMessage, + type SIWxSigner, + type EVMSigner, + type SolanaSigner, +} from "./sign"; + +// Chain utilities - EVM +export { formatSIWEMessage, verifyEVMSignature, extractEVMChainId } from "./evm"; + +// Chain utilities - Solana +export { formatSIWSMessage, verifySolanaSignature, decodeBase58, encodeBase58, extractSolanaNetwork } from "./solana"; diff --git a/typescript/packages/extensions/src/sign-in-with-x/message.ts b/typescript/packages/extensions/src/sign-in-with-x/message.ts index 8e2c369f0..29e40e7eb 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/message.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/message.ts @@ -5,7 +5,7 @@ * Routes to chain-specific formatters based on chainId namespace. */ -import { SiweMessage } from "siwe"; +import { formatSIWEMessage } from "./evm"; import { formatSIWSMessage } from "./solana"; import type { SIWxExtensionInfo } from "./types"; @@ -34,7 +34,7 @@ import type { SIWxExtensionInfo } from "./types"; export function createSIWxMessage(serverInfo: SIWxExtensionInfo, address: string): string { // Route by chain namespace if (serverInfo.chainId.startsWith("eip155:")) { - return createEVMMessage(serverInfo, address); + return formatSIWEMessage(serverInfo, address); } if (serverInfo.chainId.startsWith("solana:")) { @@ -46,33 +46,3 @@ export function createSIWxMessage(serverInfo: SIWxExtensionInfo, address: string `Supported: eip155:* (EVM), solana:* (Solana)`, ); } - -/** - * Create EIP-4361 (SIWE) compliant message for EVM chains. - * - * Uses the siwe library to ensure message format matches verification. - */ -function createEVMMessage(serverInfo: SIWxExtensionInfo, address: string): string { - const chainIdMatch = /^eip155:(\d+)$/.exec(serverInfo.chainId); - if (!chainIdMatch) { - throw new Error(`Invalid EVM chainId format: ${serverInfo.chainId}. Expected eip155:`); - } - const numericChainId = parseInt(chainIdMatch[1], 10); - - const siweMessage = new SiweMessage({ - domain: serverInfo.domain, - address, - statement: serverInfo.statement, - uri: serverInfo.uri, - version: serverInfo.version, - chainId: numericChainId, - nonce: serverInfo.nonce, - issuedAt: serverInfo.issuedAt, - expirationTime: serverInfo.expirationTime, - notBefore: serverInfo.notBefore, - requestId: serverInfo.requestId, - resources: serverInfo.resources, - }); - - return siweMessage.prepareMessage(); -} diff --git a/typescript/packages/extensions/src/sign-in-with-x/sign.ts b/typescript/packages/extensions/src/sign-in-with-x/sign.ts index 8aa5fece4..a3a2e1e33 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/sign.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/sign.ts @@ -1,15 +1,18 @@ /** * Message signing for SIWX extension * - * Client-side helper for signing SIWX messages. + * Client-side helpers for signing SIWX messages. + * Supports both EVM (viem) and Solana wallet adapters. */ +import { encodeBase58 } from "./solana"; + /** - * Generic signer interface for SIWX message signing. + * Signer interface for EVM SIWX message signing. * Compatible with viem WalletClient and PrivateKeyAccount. */ -export interface SIWxSigner { - /** Sign a message and return the signature */ +export interface EVMSigner { + /** Sign a message and return hex-encoded signature */ signMessage: (args: { message: string; account?: unknown }) => Promise; /** Account object (for WalletClient) */ account?: { address: string }; @@ -18,38 +21,59 @@ export interface SIWxSigner { } /** - * Sign SIWX message with wallet. - * - * Compatible with: - * - viem WalletClient (browser wallets) - * - viem PrivateKeyAccount (server-side) - * - * @param message - CAIP-122 message string to sign - * @param signer - Wallet or account that can sign messages - * @returns Signature string - * - * @example - * ```typescript - * // With WalletClient (browser) - * const signature = await signSIWxMessage(message, walletClient); - * - * // With PrivateKeyAccount (server) - * const account = privateKeyToAccount('0x...'); - * const signature = await signSIWxMessage(message, account); - * ``` - */ -export async function signSIWxMessage( - message: string, - signer: SIWxSigner, -): Promise { - // Check if signer has an account property (WalletClient pattern) - if (signer.account) { - return signer.signMessage({ - message, - account: signer.account, - }); + * Signer interface for Solana SIWX message signing. + * Compatible with @solana/wallet-adapter and Phantom/Solflare wallet APIs. + */ +export interface SolanaSigner { + /** Sign a message and return raw signature bytes */ + signMessage: (message: Uint8Array) => Promise; + /** Solana public key (Base58 encoded string or PublicKey-like object) */ + publicKey: string | { toBase58: () => string }; +} + +/** + * Union type for SIWX signers - supports both EVM and Solana wallets. + */ +export type SIWxSigner = EVMSigner | SolanaSigner; + +/** + * Get address from an EVM signer. + */ +export function getEVMAddress(signer: EVMSigner): string { + if (signer.account?.address) { + return signer.account.address; } + if (signer.address) { + return signer.address; + } + throw new Error("EVM signer missing address"); +} - // Direct signMessage (PrivateKeyAccount pattern) +/** + * Get address from a Solana signer. + */ +export function getSolanaAddress(signer: SolanaSigner): string { + const pk = signer.publicKey; + return typeof pk === "string" ? pk : pk.toBase58(); +} + +/** + * Sign a message with an EVM wallet. + * Returns hex-encoded signature. + */ +export async function signEVMMessage(message: string, signer: EVMSigner): Promise { + if (signer.account) { + return signer.signMessage({ message, account: signer.account }); + } return signer.signMessage({ message }); } + +/** + * Sign a message with a Solana wallet. + * Returns Base58-encoded signature. + */ +export async function signSolanaMessage(message: string, signer: SolanaSigner): Promise { + const messageBytes = new TextEncoder().encode(message); + const signatureBytes = await signer.signMessage(messageBytes); + return encodeBase58(signatureBytes); +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/types.ts b/typescript/packages/extensions/src/sign-in-with-x/types.ts index 31a02dd63..ee9ce32b7 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/types.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/types.ts @@ -13,7 +13,15 @@ import { z } from "zod"; export const SIGN_IN_WITH_X = "sign-in-with-x"; /** - * Supported signature schemes per CHANGELOG-v2.md line 271 + * Supported signature schemes per CHANGELOG-v2.md line 271. + * + * NOTE: This is primarily informational. Actual signature verification + * is determined by the chainId prefix, not this field: + * - `eip155:*` chains use EVM verification (handles eip191, eip712, eip1271, eip6492 automatically) + * - `solana:*` chains use Ed25519 verification (siws) + * + * The signatureScheme field serves as a hint for clients to select + * the appropriate signing UX. */ export type SignatureScheme = | "eip191" // personal_sign (default for EVM EOAs) @@ -50,7 +58,10 @@ export interface SIWxExtensionInfo { requestId?: string; /** Associated resources */ resources?: string[]; - /** Signature scheme hint */ + /** + * Signature scheme hint (informational only). + * Verification auto-detects from chainId prefix, not this field. + */ signatureScheme?: SignatureScheme; } @@ -145,7 +156,10 @@ export interface DeclareSIWxOptions { network: `eip155:${string}` | `solana:${string}` | (string & {}); /** Optional explicit expiration time */ expirationTime?: string; - /** Signature scheme hint */ + /** + * Signature scheme hint (informational only). + * Passed to clients as UX hint but does not affect verification. + */ signatureScheme?: SignatureScheme; } @@ -176,13 +190,3 @@ export interface SIWxVerifyResult { address?: string; error?: string; } - -/** - * Options for signature verification - */ -export interface SIWxVerifyOptions { - /** Web3 provider for EIP-1271/6492 smart wallet verification */ - provider?: unknown; - /** Enable smart wallet verification */ - checkSmartWallet?: boolean; -} diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index 6ead5af82..7a12c3600 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -6,10 +6,9 @@ * - Solana (solana:*): Uses Ed25519 signature verification via tweetnacl */ -import { verifyMessage } from "viem"; -import { SiweMessage } from "siwe"; +import { formatSIWEMessage, verifyEVMSignature } from "./evm"; import { formatSIWSMessage, verifySolanaSignature, decodeBase58 } from "./solana"; -import type { SIWxPayload, SIWxVerifyResult, SIWxVerifyOptions } from "./types"; +import type { SIWxPayload, SIWxVerifyResult } from "./types"; /** * Verify SIWX signature cryptographically. @@ -20,7 +19,6 @@ import type { SIWxPayload, SIWxVerifyResult, SIWxVerifyOptions } from "./types"; * - `solana:*` → Ed25519 signature verification * * @param payload - The SIWX payload containing signature - * @param options - Verification options (primarily for EVM) * @returns Verification result with recovered address if valid * * @example @@ -37,12 +35,11 @@ import type { SIWxPayload, SIWxVerifyResult, SIWxVerifyOptions } from "./types"; */ export async function verifySIWxSignature( payload: SIWxPayload, - options: SIWxVerifyOptions = {}, ): Promise { try { // Route by chain namespace if (payload.chainId.startsWith("eip155:")) { - return verifyEVMPayload(payload, options); + return verifyEVMPayload(payload); } if (payload.chainId.startsWith("solana:")) { @@ -69,45 +66,27 @@ export async function verifySIWxSignature( * - EIP-1271 (deployed smart contract wallets) * - EIP-6492 (counterfactual/pre-deploy smart wallets) */ -async function verifyEVMPayload( - payload: SIWxPayload, - _options: SIWxVerifyOptions, -): Promise { - const chainIdMatch = /^eip155:(\d+)$/.exec(payload.chainId); - if (!chainIdMatch) { - return { - valid: false, - error: `Invalid EVM chainId format: ${payload.chainId}. Expected eip155:`, - }; - } - const numericChainId = parseInt(chainIdMatch[1], 10); - +async function verifyEVMPayload(payload: SIWxPayload): Promise { // Reconstruct SIWE message for verification - const siweMessage = new SiweMessage({ - domain: payload.domain, - address: payload.address, - statement: payload.statement, - uri: payload.uri, - version: payload.version, - chainId: numericChainId, - nonce: payload.nonce, - issuedAt: payload.issuedAt, - expirationTime: payload.expirationTime, - notBefore: payload.notBefore, - requestId: payload.requestId, - resources: payload.resources, - }); - - const message = siweMessage.prepareMessage(); - - // Use viem's verifyMessage for EIP-6492 smart wallet support - // This handles EOA, EIP-1271, and EIP-6492 signatures automatically + const message = formatSIWEMessage( + { + domain: payload.domain, + uri: payload.uri, + statement: payload.statement, + version: payload.version, + chainId: payload.chainId, + nonce: payload.nonce, + issuedAt: payload.issuedAt, + expirationTime: payload.expirationTime, + notBefore: payload.notBefore, + requestId: payload.requestId, + resources: payload.resources, + }, + payload.address, + ); + try { - const valid = await verifyMessage({ - address: payload.address as `0x${string}`, - message, - signature: payload.signature as `0x${string}`, - }); + const valid = await verifyEVMSignature(message, payload.address, payload.signature); if (!valid) { return { diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts index 85bb77807..50492e211 100644 --- a/typescript/packages/extensions/test/sign-in-with-x.test.ts +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -19,6 +19,11 @@ import { encodeBase58, extractSolanaNetwork, verifySolanaSignature, + getEVMAddress, + getSolanaAddress, + signSolanaMessage, + type SolanaSigner, + type EVMSigner, } from "../src/sign-in-with-x/index"; import { safeBase64Encode } from "@x402/core/utils"; import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; @@ -441,4 +446,187 @@ describe("Sign-In-With-X Extension", () => { expect(result.error).toContain("Invalid signature length"); }); }); + + describe("Solana client-side signing", () => { + describe("getSolanaAddress", () => { + it("should get address from string publicKey", () => { + const signer: SolanaSigner = { + signMessage: async () => new Uint8Array(64), + publicKey: "BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW", + }; + expect(getSolanaAddress(signer)).toBe("BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); + }); + + it("should get address from PublicKey object", () => { + const signer: SolanaSigner = { + signMessage: async () => new Uint8Array(64), + publicKey: { toBase58: () => "BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW" }, + }; + expect(getSolanaAddress(signer)).toBe("BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); + }); + }); + + describe("getEVMAddress", () => { + it("should get address from account property", () => { + const signer: EVMSigner = { + signMessage: async () => "0x...", + account: { address: "0x1234567890123456789012345678901234567890" }, + }; + expect(getEVMAddress(signer)).toBe("0x1234567890123456789012345678901234567890"); + }); + + it("should get address from direct address property", () => { + const signer: EVMSigner = { + signMessage: async () => "0x...", + address: "0xabcdef1234567890123456789012345678901234", + }; + expect(getEVMAddress(signer)).toBe("0xabcdef1234567890123456789012345678901234"); + }); + + it("should throw for signer without address", () => { + const signer: EVMSigner = { + signMessage: async () => "0x...", + }; + expect(() => getEVMAddress(signer)).toThrow("EVM signer missing address"); + }); + }); + + describe("signSolanaMessage", () => { + it("should sign and return Base58 encoded signature", async () => { + const keypair = nacl.sign.keyPair(); + + const solanaSigner: SolanaSigner = { + signMessage: async (msg: Uint8Array) => nacl.sign.detached(msg, keypair.secretKey), + publicKey: encodeBase58(keypair.publicKey), + }; + + const message = "Test message for Solana signing"; + const signature = await signSolanaMessage(message, solanaSigner); + + // Signature should be Base58 encoded + const decoded = decodeBase58(signature); + expect(decoded.length).toBe(64); // Ed25519 signature + + // Verify the signature works + const valid = verifySolanaSignature(message, decoded, keypair.publicKey); + expect(valid).toBe(true); + }); + }); + + describe("createSIWxPayload with Solana signer", () => { + it("should create valid payload with Solana signer", async () => { + const keypair = nacl.sign.keyPair(); + const address = encodeBase58(keypair.publicKey); + + const solanaSigner: SolanaSigner = { + signMessage: async (msg: Uint8Array) => nacl.sign.detached(msg, keypair.secretKey), + publicKey: address, + }; + + const serverInfo = { + domain: "api.example.com", + uri: "https://api.example.com/data", + version: "1", + chainId: SOLANA_MAINNET, + nonce: "test123456789", + issuedAt: new Date().toISOString(), + }; + + const payload = await createSIWxPayload(serverInfo, solanaSigner); + + expect(payload.address).toBe(address); + expect(payload.chainId).toBe(SOLANA_MAINNET); + + // Verify the signature is valid + const result = await verifySIWxSignature(payload); + expect(result.valid).toBe(true); + expect(result.address).toBe(address); + }); + + it("should roundtrip through encode/parse/verify with Solana", async () => { + const keypair = nacl.sign.keyPair(); + const address = encodeBase58(keypair.publicKey); + + const solanaSigner: SolanaSigner = { + signMessage: async (msg: Uint8Array) => nacl.sign.detached(msg, keypair.secretKey), + publicKey: address, + }; + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: SOLANA_MAINNET, + statement: "Sign in to access", + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, solanaSigner); + const header = encodeSIWxHeader(payload); + const parsed = parseSIWxHeader(header); + + const validation = await validateSIWxMessage(parsed, "https://api.example.com/resource"); + expect(validation.valid).toBe(true); + + const verification = await verifySIWxSignature(parsed); + expect(verification.valid).toBe(true); + expect(verification.address).toBe(address); + }); + + it("should work with PublicKey object style signer", async () => { + const keypair = nacl.sign.keyPair(); + const address = encodeBase58(keypair.publicKey); + + // Mimic @solana/wallet-adapter style + const solanaSigner: SolanaSigner = { + signMessage: async (msg: Uint8Array) => nacl.sign.detached(msg, keypair.secretKey), + publicKey: { toBase58: () => address }, + }; + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: SOLANA_DEVNET, + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, solanaSigner); + + expect(payload.address).toBe(address); + expect(payload.chainId).toBe(SOLANA_DEVNET); + + const verification = await verifySIWxSignature(payload); + expect(verification.valid).toBe(true); + }); + }); + + describe("signatureScheme behavior", () => { + it("verification ignores signatureScheme and uses chainId", async () => { + // This test documents that signatureScheme is a hint only + const keypair = nacl.sign.keyPair(); + const address = encodeBase58(keypair.publicKey); + + const serverInfo = { + domain: "api.example.com", + uri: "https://api.example.com", + version: "1", + chainId: SOLANA_MAINNET, + nonce: "test12345", + issuedAt: new Date().toISOString(), + signatureScheme: "eip191" as const, // Wrong hint - should be "siws" + }; + + // Create message and sign + const message = formatSIWSMessage(serverInfo, address); + const messageBytes = new TextEncoder().encode(message); + const signatureBytes = nacl.sign.detached(messageBytes, keypair.secretKey); + + const payload = { + ...serverInfo, + address, + signature: encodeBase58(signatureBytes), + signatureScheme: "eip191" as const, // Wrong hint + }; + + // Verification should still work because it uses chainId, not signatureScheme + const result = await verifySIWxSignature(payload); + expect(result.valid).toBe(true); // Proves signatureScheme is ignored + }); + }); + }); }); From edfbac0d82fbd5787a8310669cc3d58e6efb46c8 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 14:20:33 -0500 Subject: [PATCH 10/20] mv solana constants --- .../packages/extensions/src/sign-in-with-x/index.ts | 3 ++- .../packages/extensions/src/sign-in-with-x/solana.ts | 8 ++++++++ .../packages/extensions/src/sign-in-with-x/types.ts | 8 -------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 3c35543ec..3aa1cf813 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -68,7 +68,8 @@ */ // Constants -export { SIGN_IN_WITH_X, SOLANA_MAINNET, SOLANA_DEVNET, SOLANA_TESTNET, SIWxPayloadSchema } from "./types"; +export { SIGN_IN_WITH_X, SIWxPayloadSchema } from "./types"; +export { SOLANA_MAINNET, SOLANA_DEVNET, SOLANA_TESTNET } from "./solana"; // Types export type { diff --git a/typescript/packages/extensions/src/sign-in-with-x/solana.ts b/typescript/packages/extensions/src/sign-in-with-x/solana.ts index b433f8a81..14a2fe844 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/solana.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/solana.ts @@ -9,6 +9,14 @@ import { base58 } from "@scure/base"; import nacl from "tweetnacl"; import type { SIWxExtensionInfo } from "./types"; +/** + * Common Solana network CAIP-2 identifiers. + * Uses genesis hash as the chain reference per CAIP-30. + */ +export const SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; +export const SOLANA_DEVNET = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"; +export const SOLANA_TESTNET = "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z"; + /** * Known Solana network genesis hashes mapped to human-readable names. * Used for Chain ID display in SIWS messages. diff --git a/typescript/packages/extensions/src/sign-in-with-x/types.ts b/typescript/packages/extensions/src/sign-in-with-x/types.ts index ee9ce32b7..752bc35bc 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/types.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/types.ts @@ -128,14 +128,6 @@ export const SIWxPayloadSchema = z.object({ */ export type SIWxPayload = z.infer; -/** - * Common Solana network CAIP-2 identifiers. - * Uses genesis hash as the chain reference per CAIP-30. - */ -export const SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"; -export const SOLANA_DEVNET = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"; -export const SOLANA_TESTNET = "solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z"; - /** * Options for declaring SIWX extension on server */ From 02d45cd6ddb3a84ce05123805c90d53b60536b49 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 14:30:48 -0500 Subject: [PATCH 11/20] fmt --- .../packages/extensions/src/sign-in-with-x/client.ts | 7 +------ .../packages/extensions/src/sign-in-with-x/index.ts | 8 +++++++- .../packages/extensions/src/sign-in-with-x/types.ts | 4 +--- .../packages/extensions/src/sign-in-with-x/verify.ts | 4 +--- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/typescript/packages/extensions/src/sign-in-with-x/client.ts b/typescript/packages/extensions/src/sign-in-with-x/client.ts index 99efab01e..8cc26e1cf 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/client.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/client.ts @@ -7,12 +7,7 @@ import type { SIWxExtensionInfo, SIWxPayload } from "./types"; import type { SIWxSigner, EVMSigner, SolanaSigner } from "./sign"; -import { - getEVMAddress, - getSolanaAddress, - signEVMMessage, - signSolanaMessage, -} from "./sign"; +import { getEVMAddress, getSolanaAddress, signEVMMessage, signSolanaMessage } from "./sign"; import { createSIWxMessage } from "./message"; /** diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 3aa1cf813..2019c6b3a 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -109,4 +109,10 @@ export { export { formatSIWEMessage, verifyEVMSignature, extractEVMChainId } from "./evm"; // Chain utilities - Solana -export { formatSIWSMessage, verifySolanaSignature, decodeBase58, encodeBase58, extractSolanaNetwork } from "./solana"; +export { + formatSIWSMessage, + verifySolanaSignature, + decodeBase58, + encodeBase58, + extractSolanaNetwork, +} from "./solana"; diff --git a/typescript/packages/extensions/src/sign-in-with-x/types.ts b/typescript/packages/extensions/src/sign-in-with-x/types.ts index 752bc35bc..c7ca54d37 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/types.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/types.ts @@ -117,9 +117,7 @@ export const SIWxPayloadSchema = z.object({ notBefore: z.string().optional(), requestId: z.string().optional(), resources: z.array(z.string()).optional(), - signatureScheme: z - .enum(["eip191", "eip712", "eip1271", "eip6492", "siws", "sep10"]) - .optional(), + signatureScheme: z.enum(["eip191", "eip712", "eip1271", "eip6492", "siws", "sep10"]).optional(), signature: z.string(), }); diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index 7a12c3600..3237ee26f 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -33,9 +33,7 @@ import type { SIWxPayload, SIWxVerifyResult } from "./types"; * } * ``` */ -export async function verifySIWxSignature( - payload: SIWxPayload, -): Promise { +export async function verifySIWxSignature(payload: SIWxPayload): Promise { try { // Route by chain namespace if (payload.chainId.startsWith("eip155:")) { From 2af8526fd45e2eff4327f0b88413c45ba230c694 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 14:40:08 -0500 Subject: [PATCH 12/20] lint --- .../extensions/src/sign-in-with-x/index.ts | 2 +- .../extensions/src/sign-in-with-x/schema.ts | 2 ++ .../packages/extensions/src/sign-in-with-x/sign.ts | 14 ++++++++++++++ .../extensions/src/sign-in-with-x/verify.ts | 6 ++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 2019c6b3a..685df72c9 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -64,7 +64,7 @@ * fetch(url, { headers: { 'SIGN-IN-WITH-X': header } }); * ``` * - * @packageDocumentation + * @module sign-in-with-x */ // Constants diff --git a/typescript/packages/extensions/src/sign-in-with-x/schema.ts b/typescript/packages/extensions/src/sign-in-with-x/schema.ts index be067fe7f..6fb06b73d 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/schema.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/schema.ts @@ -9,6 +9,8 @@ import type { SIWxExtensionSchema } from "./types"; /** * Build JSON Schema for SIWX extension validation. * This schema validates the client proof payload structure. + * + * @returns JSON Schema for validating SIWX client payloads */ export function buildSIWxSchema(): SIWxExtensionSchema { return { diff --git a/typescript/packages/extensions/src/sign-in-with-x/sign.ts b/typescript/packages/extensions/src/sign-in-with-x/sign.ts index a3a2e1e33..a2b3afc28 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/sign.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/sign.ts @@ -38,6 +38,9 @@ export type SIWxSigner = EVMSigner | SolanaSigner; /** * Get address from an EVM signer. + * + * @param signer - EVM wallet signer instance + * @returns The wallet address as a hex string */ export function getEVMAddress(signer: EVMSigner): string { if (signer.account?.address) { @@ -51,6 +54,9 @@ export function getEVMAddress(signer: EVMSigner): string { /** * Get address from a Solana signer. + * + * @param signer - Solana wallet signer instance + * @returns The wallet address as a Base58 string */ export function getSolanaAddress(signer: SolanaSigner): string { const pk = signer.publicKey; @@ -60,6 +66,10 @@ export function getSolanaAddress(signer: SolanaSigner): string { /** * Sign a message with an EVM wallet. * Returns hex-encoded signature. + * + * @param message - The message to sign + * @param signer - EVM wallet signer instance + * @returns Hex-encoded signature */ export async function signEVMMessage(message: string, signer: EVMSigner): Promise { if (signer.account) { @@ -71,6 +81,10 @@ export async function signEVMMessage(message: string, signer: EVMSigner): Promis /** * Sign a message with a Solana wallet. * Returns Base58-encoded signature. + * + * @param message - The message to sign + * @param signer - Solana wallet signer instance + * @returns Base58-encoded signature */ export async function signSolanaMessage(message: string, signer: SolanaSigner): Promise { const messageBytes = new TextEncoder().encode(message); diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index 3237ee26f..bc960f064 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -63,6 +63,9 @@ export async function verifySIWxSignature(payload: SIWxPayload): Promise { // Reconstruct SIWE message for verification @@ -109,6 +112,9 @@ async function verifyEVMPayload(payload: SIWxPayload): Promise * Verify Solana Ed25519 signature. * * Reconstructs the SIWS message and verifies using tweetnacl. + * + * @param payload - The SIWX payload containing signature and message data + * @returns Verification result with recovered address if valid */ function verifySolanaPayload(payload: SIWxPayload): SIWxVerifyResult { // Reconstruct SIWS message From eec4d83a03e99c87481257160b9cf33ae98fb8ef Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 14:55:50 -0500 Subject: [PATCH 13/20] rm .claude --- .claude/1_siwx_extension.md | 694 ------------------------------------ 1 file changed, 694 deletions(-) delete mode 100644 .claude/1_siwx_extension.md diff --git a/.claude/1_siwx_extension.md b/.claude/1_siwx_extension.md deleted file mode 100644 index 4061046bc..000000000 --- a/.claude/1_siwx_extension.md +++ /dev/null @@ -1,694 +0,0 @@ -# Sign-In-With-X (SIWX) Extension Implementation Plan - -## Overview - -Implement the Sign-In-With-X (SIWX) extension for x402 v2, providing CAIP-122 standard wallet-based identity assertions. This extension allows clients to prove control of a wallet that may have previously paid for a resource, enabling servers to grant access without requiring repurchase. - -**Spec References:** -- `typescript/site/CHANGELOG-v2.md` (lines 237-341) -- `typescript/packages/extensions/src/sign-in-with-x/TODO.md` - -## Architecture - -SIWX is a **server-client extension** (no facilitator involvement required per spec line 471). It follows the standard extension pattern with `info` + `schema` structure. - -### Data Flow - -``` -Server Client - | | - |-- 402 PaymentRequired ----------->| - | extensions: { | - | sign-in-with-x: {info, schema}| - | } | - | | - |<-- SIGN-IN-WITH-X header ---------| - | (base64-encoded JSON proof) | - | | - |-- Verify signature locally -------| - |-- 200 Success ------------------->| -``` - -## File Structure - -``` -typescript/packages/extensions/src/sign-in-with-x/ -├── index.ts # Public exports -├── types.ts # Type definitions + SIGN_IN_WITH_X constant -├── schema.ts # JSON Schema builder -├── server.ts # ResourceServerExtension hook -├── declare.ts # declareSIWxExtension() - server declaration helper -├── parse.ts # parseSIWxHeader() - extract from request header -├── validate.ts # validateSIWxMessage() - field/temporal validation -├── verify.ts # verifySIWxSignature() - crypto verification -├── message.ts # createSIWxMessage() - CAIP-122 message construction -├── sign.ts # signSIWxMessage() - client signing -├── encode.ts # encodeSIWxHeader() - base64 encode for header -└── client.ts # createSIWxPayload() - complete client flow -``` - -## Type Definitions (`types.ts`) - -```typescript -// Extension identifier -export const SIGN_IN_WITH_X = 'sign-in-with-x'; - -// Signature schemes per CHANGELOG-v2.md -export type SignatureScheme = - | 'eip191' // personal_sign (default for EVM EOAs) - | 'eip712' // typed data signing - | 'eip1271' // smart contract wallet verification - | 'eip6492' // counterfactual smart wallet verification - | 'siws' // Sign-In-With-Solana - | 'sep10'; // Stellar SEP-10 - -// Server declares in PaymentRequired.extensions -export interface SIWxExtensionInfo { - domain: string; // Server's domain (derived from resourceUri) - uri: string; // Full resource URI - statement?: string; // Human-readable purpose - version: string; // Always "1" per CAIP-122 - chainId: string; // CAIP-2 format: "eip155:8453" - nonce: string; // Cryptographic random (SDK auto-generates) - issuedAt: string; // ISO 8601 (SDK auto-generates) - expirationTime?: string; // Optional expiry (default: +5 min) - notBefore?: string; // Optional validity start - requestId?: string; // Optional correlation ID - resources?: string[]; // Associated resources - signatureScheme?: SignatureScheme; -} - -export interface SIWxExtensionSchema { - $schema: string; - type: 'object'; - properties: Record; - required: string[]; -} - -export interface SIWxExtension { - info: SIWxExtensionInfo; - schema: SIWxExtensionSchema; -} - -// Client proof (sent in SIGN-IN-WITH-X header) -export interface SIWxPayload extends SIWxExtensionInfo { - address: string; // Signing wallet address - signature: string; // Cryptographic signature -} - -// Server declaration options -export interface DeclareSIWxOptions { - resourceUri: string; - statement?: string; - version?: string; // Default: "1" - network: `eip155:${string}` | `solana:${string}` | string; - expirationTime?: string; // Default: auto +5 min - signatureScheme?: SignatureScheme; -} -``` - -## Server-Side Implementation - -### 1. Declaration Helper (`declare.ts`) - -```typescript -import { randomBytes } from 'crypto'; -import type { SIWxExtension, DeclareSIWxOptions } from './types'; -import { SIGN_IN_WITH_X } from './types'; -import { buildSIWxSchema } from './schema'; - -/** - * Server helper to declare SIWX authentication requirement. - * Auto-generates nonce, issuedAt, and derives domain from URI. - */ -export function declareSIWxExtension( - options: DeclareSIWxOptions -): Record { - const url = new URL(options.resourceUri); - const nonce = randomBytes(16).toString('hex'); - const issuedAt = new Date().toISOString(); - const expirationTime = options.expirationTime ?? - new Date(Date.now() + 5 * 60 * 1000).toISOString(); - - const info: SIWxExtensionInfo = { - domain: url.host, - uri: options.resourceUri, - statement: options.statement, - version: options.version ?? '1', - chainId: options.network, - nonce, - issuedAt, - expirationTime, - resources: [options.resourceUri], - ...(options.signatureScheme && { signatureScheme: options.signatureScheme }), - }; - - return { - [SIGN_IN_WITH_X]: { - info, - schema: buildSIWxSchema(), - }, - }; -} -``` - -### 2. JSON Schema Builder (`schema.ts`) - -```typescript -import type { SIWxExtensionSchema } from './types'; - -/** - * Build JSON Schema for SIWX extension validation. - * Per CHANGELOG-v2.md lines 276-292. - */ -export function buildSIWxSchema(): SIWxExtensionSchema { - return { - $schema: 'https://json-schema.org/draft/2020-12/schema', - type: 'object', - properties: { - domain: { type: 'string' }, - address: { type: 'string' }, - statement: { type: 'string' }, - uri: { type: 'string', format: 'uri' }, - version: { type: 'string' }, - chainId: { type: 'string' }, - nonce: { type: 'string' }, - issuedAt: { type: 'string', format: 'date-time' }, - expirationTime: { type: 'string', format: 'date-time' }, - notBefore: { type: 'string', format: 'date-time' }, - requestId: { type: 'string' }, - resources: { type: 'array', items: { type: 'string', format: 'uri' } }, - signature: { type: 'string' }, - }, - required: ['domain', 'address', 'uri', 'version', 'chainId', 'nonce', 'issuedAt', 'signature'], - }; -} -``` - -### 3. Header Parsing (`parse.ts`) - -```typescript -import type { SIWxPayload } from './types'; - -/** - * Parse SIGN-IN-WITH-X header into structured payload. - * Supports both base64-encoded (spec) and raw JSON (backwards compat). - */ -export function parseSIWxHeader(header: string): SIWxPayload { - let jsonStr: string; - - // Try base64 decode first (spec-compliant) - try { - jsonStr = Buffer.from(header, 'base64').toString('utf-8'); - // Verify it's valid JSON - JSON.parse(jsonStr); - } catch { - // Fall back to raw JSON (backwards compatibility) - jsonStr = header; - } - - try { - const payload = JSON.parse(jsonStr) as SIWxPayload; - - // Validate required fields per schema - const required = ['domain', 'address', 'uri', 'version', 'chainId', 'nonce', 'issuedAt', 'signature']; - const missing = required.filter(f => !(f in payload) || !payload[f as keyof SIWxPayload]); - - if (missing.length > 0) { - throw new Error(`Missing required fields: ${missing.join(', ')}`); - } - - return payload; - } catch (error) { - if (error instanceof SyntaxError) { - throw new Error('Invalid SIWX header: not valid JSON or base64'); - } - throw error; - } -} -``` - -### 4. Message Validation (`validate.ts`) - -```typescript -import type { SIWxPayload } from './types'; - -const MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes per spec - -export interface ValidationOptions { - maxAge?: number; - checkNonce?: (nonce: string) => boolean | Promise; -} - -export interface ValidationResult { - valid: boolean; - error?: string; -} - -/** - * Validate SIWX message fields (before signature verification). - * Per CHANGELOG-v2.md validation rules (lines 318-329). - */ -export async function validateSIWxMessage( - message: SIWxPayload, - expectedResourceUri: string, - options: ValidationOptions = {} -): Promise { - const expectedUrl = new URL(expectedResourceUri); - const maxAge = options.maxAge ?? MAX_AGE_MS; - - // 1. Domain binding (spec: "domain field MUST match server's domain") - if (message.domain !== expectedUrl.host) { - return { valid: false, error: `Domain mismatch: expected ${expectedUrl.host}, got ${message.domain}` }; - } - - // 2. URI validation (spec: "uri and resources must refer to base url") - if (!message.uri.startsWith(expectedUrl.origin)) { - return { valid: false, error: `URI mismatch: expected ${expectedUrl.origin}` }; - } - - // 3. issuedAt validation (spec: "MUST be recent, recommended < 5 minutes") - const issuedAt = new Date(message.issuedAt); - if (isNaN(issuedAt.getTime())) { - return { valid: false, error: 'Invalid issuedAt timestamp' }; - } - - const age = Date.now() - issuedAt.getTime(); - if (age > maxAge) { - return { valid: false, error: `Message too old: ${Math.round(age/1000)}s > ${maxAge/1000}s limit` }; - } - if (age < -60000) { // Allow 1 min clock skew - return { valid: false, error: 'issuedAt is in the future' }; - } - - // 4. expirationTime validation (spec: "MUST be in the future") - if (message.expirationTime) { - const expiration = new Date(message.expirationTime); - if (isNaN(expiration.getTime())) { - return { valid: false, error: 'Invalid expirationTime' }; - } - if (expiration < new Date()) { - return { valid: false, error: 'Message expired' }; - } - } - - // 5. notBefore validation (spec: "if present, MUST be in the past") - if (message.notBefore) { - const notBefore = new Date(message.notBefore); - if (new Date() < notBefore) { - return { valid: false, error: 'Message not yet valid (notBefore)' }; - } - } - - // 6. Nonce validation (spec: "MUST be unique per session") - if (options.checkNonce) { - const nonceValid = await options.checkNonce(message.nonce); - if (!nonceValid) { - return { valid: false, error: 'Nonce validation failed (replay detected)' }; - } - } - - return { valid: true }; -} -``` - -### 5. Signature Verification (`verify.ts`) - -```typescript -import { SiweMessage } from 'siwe'; -import type { SIWxPayload, SignatureScheme } from './types'; - -export interface VerifyOptions { - provider?: unknown; // Web3 provider for EIP-1271/6492 - checkSmartWallet?: boolean; -} - -export interface VerifyResult { - valid: boolean; - address?: string; // Recovered/verified address (checksummed) - error?: string; -} - -/** - * Verify SIWX signature cryptographically. - * Currently supports EVM (eip191). Extensible for other schemes. - */ -export async function verifySIWxSignature( - payload: SIWxPayload, - options: VerifyOptions = {} -): Promise { - try { - // Parse CAIP-2 chainId - const chainIdMatch = /^eip155:(\d+)$/.exec(payload.chainId); - if (!chainIdMatch) { - // TODO: Add support for solana:*, etc. - return { valid: false, error: `Unsupported chainId namespace: ${payload.chainId}` }; - } - const numericChainId = parseInt(chainIdMatch[1], 10); - - // Reconstruct SIWE message - const siweMessage = new SiweMessage({ - domain: payload.domain, - address: payload.address, - statement: payload.statement, - uri: payload.uri, - version: payload.version, - chainId: numericChainId, - nonce: payload.nonce, - issuedAt: payload.issuedAt, - expirationTime: payload.expirationTime, - notBefore: payload.notBefore, - requestId: payload.requestId, - resources: payload.resources, - }); - - // Verify signature - const result = await siweMessage.verify({ - signature: payload.signature, - ...(options.checkSmartWallet && options.provider && { provider: options.provider }), - }); - - if (!result.success) { - return { valid: false, error: result.error?.message ?? 'Signature verification failed' }; - } - - return { valid: true, address: siweMessage.address }; - } catch (error) { - return { - valid: false, - error: error instanceof Error ? error.message : 'Verification failed', - }; - } -} -``` - -### 6. ResourceServerExtension Hook (`server.ts`) - -```typescript -import type { ResourceServerExtension } from '@x402/core/types'; -import { SIGN_IN_WITH_X } from './types'; - -/** - * Extension hook for x402ResourceServer.registerExtension(). - * Can enrich declarations based on transport context if needed. - */ -export const siwxResourceServerExtension: ResourceServerExtension = { - key: SIGN_IN_WITH_X, - - enrichDeclaration: (declaration, _transportContext) => { - // Pass through - server explicitly declares SIWX requirements - // Could auto-derive domain from HTTP context in future - return declaration; - }, -}; -``` - -## Client-Side Implementation - -### 7. Message Construction (`message.ts`) - -```typescript -import type { SIWxExtensionInfo } from './types'; - -/** - * Construct CAIP-122 compliant message string for signing. - * Format per https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-122.md - */ -export function createSIWxMessage( - serverInfo: SIWxExtensionInfo, - address: string -): string { - const lines: string[] = []; - - // Header - lines.push(`${serverInfo.domain} wants you to sign in with your ${serverInfo.chainId} account:`); - lines.push(address); - lines.push(''); - - // Statement (optional) - if (serverInfo.statement) { - lines.push(serverInfo.statement); - lines.push(''); - } - - // Required fields - lines.push(`URI: ${serverInfo.uri}`); - lines.push(`Version: ${serverInfo.version}`); - lines.push(`Chain ID: ${serverInfo.chainId}`); - lines.push(`Nonce: ${serverInfo.nonce}`); - lines.push(`Issued At: ${serverInfo.issuedAt}`); - - // Optional fields - if (serverInfo.expirationTime) { - lines.push(`Expiration Time: ${serverInfo.expirationTime}`); - } - if (serverInfo.notBefore) { - lines.push(`Not Before: ${serverInfo.notBefore}`); - } - if (serverInfo.requestId) { - lines.push(`Request ID: ${serverInfo.requestId}`); - } - - // Resources - if (serverInfo.resources && serverInfo.resources.length > 0) { - lines.push('Resources:'); - for (const resource of serverInfo.resources) { - lines.push(`- ${resource}`); - } - } - - return lines.join('\n'); -} -``` - -### 8. Signing Helper (`sign.ts`) - -```typescript -import type { WalletClient } from 'viem'; -import type { PrivateKeyAccount } from 'viem/accounts'; - -export interface SignOptions { - // Future: signatureScheme for EIP-712, etc. -} - -/** - * Sign SIWX message with wallet. - */ -export async function signSIWxMessage( - message: string, - signer: WalletClient | PrivateKeyAccount -): Promise { - // WalletClient (browser wallets) - if ('account' in signer && 'signMessage' in signer) { - const wallet = signer as WalletClient; - if (!wallet.account) { - throw new Error('Wallet account not connected'); - } - return wallet.signMessage({ - account: wallet.account, - message, - }); - } - - // PrivateKeyAccount (server-side) - if ('signMessage' in signer) { - return (signer as PrivateKeyAccount).signMessage({ message }); - } - - throw new Error('Invalid signer type'); -} -``` - -### 9. Header Encoding (`encode.ts`) - -```typescript -import type { SIWxPayload } from './types'; - -/** - * Encode SIWX payload for SIGN-IN-WITH-X header. - * Uses base64 encoding per CHANGELOG-v2.md line 335. - */ -export function encodeSIWxHeader(payload: SIWxPayload): string { - const json = JSON.stringify(payload); - return Buffer.from(json).toString('base64'); -} - -/** - * Encode SIWX payload as raw JSON (for environments without base64 requirement). - */ -export function encodeSIWxHeaderRaw(payload: SIWxPayload): string { - return JSON.stringify(payload); -} -``` - -### 10. Complete Client Flow (`client.ts`) - -```typescript -import type { WalletClient } from 'viem'; -import type { PrivateKeyAccount } from 'viem/accounts'; -import type { SIWxExtensionInfo, SIWxPayload } from './types'; -import { createSIWxMessage } from './message'; -import { signSIWxMessage } from './sign'; - -/** - * Complete client flow: construct message, sign, return payload. - */ -export async function createSIWxPayload( - serverExtension: SIWxExtensionInfo, - signer: WalletClient | PrivateKeyAccount -): Promise { - // Get address from signer - let address: string; - if ('account' in signer && signer.account) { - address = signer.account.address; - } else if ('address' in signer) { - address = (signer as PrivateKeyAccount).address; - } else { - throw new Error('Cannot determine signer address'); - } - - // Construct CAIP-122 message - const message = createSIWxMessage(serverExtension, address); - - // Sign message - const signature = await signSIWxMessage(message, signer); - - // Return complete payload - return { - domain: serverExtension.domain, - address, - statement: serverExtension.statement, - uri: serverExtension.uri, - version: serverExtension.version, - chainId: serverExtension.chainId, - nonce: serverExtension.nonce, - issuedAt: serverExtension.issuedAt, - expirationTime: serverExtension.expirationTime, - notBefore: serverExtension.notBefore, - requestId: serverExtension.requestId, - resources: serverExtension.resources, - signature, - }; -} -``` - -## Public Exports (`index.ts`) - -```typescript -/** - * Sign-In-With-X Extension for x402 v2 - * - * CAIP-122 compliant wallet authentication for payment-protected resources. - */ - -// Extension identifier -export { SIGN_IN_WITH_X } from './types'; - -// Types -export type { - SIWxExtension, - SIWxExtensionInfo, - SIWxExtensionSchema, - SIWxPayload, - DeclareSIWxOptions, - SignatureScheme, -} from './types'; - -// Server exports -export { declareSIWxExtension } from './declare'; -export { parseSIWxHeader } from './parse'; -export { validateSIWxMessage, type ValidationResult, type ValidationOptions } from './validate'; -export { verifySIWxSignature, type VerifyResult, type VerifyOptions } from './verify'; -export { siwxResourceServerExtension } from './server'; -export { buildSIWxSchema } from './schema'; - -// Client exports -export { createSIWxMessage } from './message'; -export { signSIWxMessage, type SignOptions } from './sign'; -export { createSIWxPayload } from './client'; -export { encodeSIWxHeader, encodeSIWxHeaderRaw } from './encode'; -``` - -## Dependencies - -Add to `typescript/packages/extensions/package.json`: - -```json -{ - "dependencies": { - "siwe": "^2.3.0" - } -} -``` - -## Testing Strategy - -```typescript -// test/sign-in-with-x.test.ts - -describe('Sign-In-With-X Extension', () => { - describe('Server Side', () => { - it('declareSIWxExtension generates valid extension with auto-fields'); - it('parseSIWxHeader handles base64 encoded input'); - it('parseSIWxHeader handles raw JSON for backwards compat'); - it('validateSIWxMessage rejects expired messages'); - it('validateSIWxMessage rejects domain mismatch'); - it('validateSIWxMessage rejects old issuedAt (> 5 min)'); - it('verifySIWxSignature validates EIP-191 signatures'); - }); - - describe('Client Side', () => { - it('createSIWxMessage generates CAIP-122 format'); - it('signSIWxMessage signs with PrivateKeyAccount'); - it('encodeSIWxHeader produces valid base64'); - }); - - describe('Integration', () => { - it('server can verify client-generated proof (full flow)'); - }); -}); -``` - -## Implementation Order - -1. **Phase 1**: `types.ts`, `schema.ts` - Foundation -2. **Phase 2**: `declare.ts`, `parse.ts` - Server basics -3. **Phase 3**: `validate.ts`, `verify.ts` - Server validation -4. **Phase 4**: `message.ts`, `sign.ts`, `encode.ts`, `client.ts` - Client side -5. **Phase 5**: `server.ts`, `index.ts` - Integration + exports -6. **Phase 6**: Tests -7. **Phase 7**: Update `tsup.config.ts` for subpath export - -## Package Export Configuration - -Update `typescript/packages/extensions/tsup.config.ts`: - -```typescript -export default defineConfig({ - entry: { - index: 'src/index.ts', - 'bazaar/index': 'src/bazaar/index.ts', - 'sign-in-with-x/index': 'src/sign-in-with-x/index.ts', // Add this - }, - // ... -}); -``` - -Update `package.json` exports: - -```json -{ - "exports": { - "./sign-in-with-x": { - "import": { - "types": "./dist/esm/sign-in-with-x/index.d.mts", - "default": "./dist/esm/sign-in-with-x/index.mjs" - }, - "require": { - "types": "./dist/cjs/sign-in-with-x/index.d.ts", - "default": "./dist/cjs/sign-in-with-x/index.js" - } - } - } -} -``` From 61bec25c5a72b2d7fd81a1153544cd224f8e6738 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 15:19:52 -0500 Subject: [PATCH 14/20] smart wallet verifier --- .../extensions/src/sign-in-with-x/evm.ts | 43 +++++-- .../extensions/src/sign-in-with-x/index.ts | 2 + .../extensions/src/sign-in-with-x/types.ts | 42 +++++++ .../extensions/src/sign-in-with-x/verify.ts | 42 ++++--- .../extensions/test/sign-in-with-x.test.ts | 110 +++++++++++++++++- 5 files changed, 214 insertions(+), 25 deletions(-) diff --git a/typescript/packages/extensions/src/sign-in-with-x/evm.ts b/typescript/packages/extensions/src/sign-in-with-x/evm.ts index 32fe004f7..39269326c 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/evm.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/evm.ts @@ -7,7 +7,7 @@ import { verifyMessage } from "viem"; import { SiweMessage } from "siwe"; -import type { SIWxExtensionInfo } from "./types"; +import type { SIWxExtensionInfo, EVMMessageVerifier } from "./types"; /** * Extract numeric chain ID from CAIP-2 EVM chainId. @@ -78,31 +78,56 @@ export function formatSIWEMessage(info: SIWxExtensionInfo, address: string): str } /** - * Verify EVM signature with EIP-6492 smart wallet support. + * Verify EVM signature. * - * Uses viem's verifyMessage which automatically handles: - * - EOA signatures (standard ECDSA via EIP-191) - * - EIP-1271 (deployed smart contract wallets) - * - EIP-6492 (counterfactual/pre-deploy smart wallets) + * Supports: + * - EOA signatures (standard ECDSA via EIP-191) - always available + * - EIP-1271 (deployed smart contract wallets) - requires verifier + * - EIP-6492 (counterfactual/pre-deploy smart wallets) - requires verifier * * @param message - The SIWE message that was signed * @param address - The claimed signer address * @param signature - The signature to verify + * @param verifier - Optional message verifier for smart wallet support. + * Pass publicClient.verifyMessage for EIP-1271/EIP-6492 support. + * Without this, only EOA signatures are verified. * @returns true if signature is valid * * @example * ```typescript - * const valid = await verifyEVMSignature(message, "0x1234...", "0xsig..."); + * // EOA-only verification (default, no RPC required) + * const valid = await verifyEVMSignature(message, address, signature); + * + * // Smart wallet verification with viem PublicClient + * import { createPublicClient, http } from 'viem'; + * import { base } from 'viem/chains'; + * + * const publicClient = createPublicClient({ chain: base, transport: http() }); + * const valid = await verifyEVMSignature( + * message, + * address, + * signature, + * publicClient.verifyMessage + * ); * ``` */ export async function verifyEVMSignature( message: string, address: string, signature: string, + verifier?: EVMMessageVerifier, ): Promise { - return verifyMessage({ + const args = { address: address as `0x${string}`, message, signature: signature as `0x${string}`, - }); + }; + + if (verifier) { + // Use provided verifier (supports EIP-1271/EIP-6492 via RPC) + return verifier(args); + } + + // Fallback to standalone verifyMessage (EOA only, no RPC) + return verifyMessage(args); } diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 685df72c9..eef5b3cbf 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -82,6 +82,8 @@ export type { SIWxValidationResult, SIWxValidationOptions, SIWxVerifyResult, + EVMMessageVerifier, + SIWxVerifyOptions, } from "./types"; // Server diff --git a/typescript/packages/extensions/src/sign-in-with-x/types.ts b/typescript/packages/extensions/src/sign-in-with-x/types.ts index c7ca54d37..9a3bdf766 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/types.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/types.ts @@ -180,3 +180,45 @@ export interface SIWxVerifyResult { address?: string; error?: string; } + +/** + * EVM message verifier function type. + * Compatible with viem's publicClient.verifyMessage(). + * + * When provided to verifySIWxSignature, enables: + * - EIP-1271 (deployed smart contract wallets) + * - EIP-6492 (counterfactual/pre-deploy smart wallets) + * + * Without a verifier, only EOA signatures (EIP-191) can be verified. + * + * @example + * ```typescript + * import { createPublicClient, http } from 'viem'; + * import { base } from 'viem/chains'; + * + * const publicClient = createPublicClient({ chain: base, transport: http() }); + * // publicClient.verifyMessage satisfies EVMMessageVerifier + * ``` + */ +export type EVMMessageVerifier = (args: { + address: `0x${string}`; + message: string; + signature: `0x${string}`; +}) => Promise; + +/** + * Options for SIWX signature verification + */ +export interface SIWxVerifyOptions { + /** + * EVM message verifier for smart wallet support. + * + * Pass `publicClient.verifyMessage` from viem to enable verification of: + * - Smart contract wallets (EIP-1271) + * - Counterfactual/undeployed smart wallets (EIP-6492) + * + * If not provided, only EOA signatures are verified using standalone + * ECDSA recovery (no RPC calls required). + */ + evmVerifier?: EVMMessageVerifier; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index bc960f064..746311c5b 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -2,30 +2,40 @@ * Signature verification for SIWX extension * * Routes to chain-specific verification based on chainId namespace: - * - EVM (eip155:*): Uses viem's verifyMessage with EIP-6492 smart wallet support - * - Solana (solana:*): Uses Ed25519 signature verification via tweetnacl + * - EVM (eip155:*): EOA by default, smart wallet (EIP-1271/EIP-6492) with verifier + * - Solana (solana:*): Ed25519 signature verification via tweetnacl */ import { formatSIWEMessage, verifyEVMSignature } from "./evm"; import { formatSIWSMessage, verifySolanaSignature, decodeBase58 } from "./solana"; -import type { SIWxPayload, SIWxVerifyResult } from "./types"; +import type { SIWxPayload, SIWxVerifyResult, SIWxVerifyOptions, EVMMessageVerifier } from "./types"; /** * Verify SIWX signature cryptographically. * * Routes to the appropriate chain-specific verification based on the * chainId namespace prefix: - * - `eip155:*` → EVM verification with EIP-6492 smart wallet support + * - `eip155:*` → EVM verification (EOA by default, smart wallet with verifier) * - `solana:*` → Ed25519 signature verification * * @param payload - The SIWX payload containing signature + * @param options - Optional verification options * @returns Verification result with recovered address if valid * * @example * ```typescript - * const payload = parseSIWxHeader(header); + * // EOA-only verification (default) * const result = await verifySIWxSignature(payload); * + * // Smart wallet verification + * import { createPublicClient, http } from 'viem'; + * import { base } from 'viem/chains'; + * + * const publicClient = createPublicClient({ chain: base, transport: http() }); + * const result = await verifySIWxSignature(payload, { + * evmVerifier: publicClient.verifyMessage, + * }); + * * if (result.valid) { * console.log('Verified wallet:', result.address); * } else { @@ -33,11 +43,14 @@ import type { SIWxPayload, SIWxVerifyResult } from "./types"; * } * ``` */ -export async function verifySIWxSignature(payload: SIWxPayload): Promise { +export async function verifySIWxSignature( + payload: SIWxPayload, + options?: SIWxVerifyOptions, +): Promise { try { // Route by chain namespace if (payload.chainId.startsWith("eip155:")) { - return verifyEVMPayload(payload); + return verifyEVMPayload(payload, options?.evmVerifier); } if (payload.chainId.startsWith("solana:")) { @@ -57,17 +70,16 @@ export async function verifySIWxSignature(payload: SIWxPayload): Promise { +async function verifyEVMPayload( + payload: SIWxPayload, + verifier?: EVMMessageVerifier, +): Promise { // Reconstruct SIWE message for verification const message = formatSIWEMessage( { @@ -87,7 +99,7 @@ async function verifyEVMPayload(payload: SIWxPayload): Promise ); try { - const valid = await verifyEVMSignature(message, payload.address, payload.signature); + const valid = await verifyEVMSignature(message, payload.address, payload.signature, verifier); if (!valid) { return { diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts index 50492e211..10aeb2735 100644 --- a/typescript/packages/extensions/test/sign-in-with-x.test.ts +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -2,7 +2,7 @@ * Tests for Sign-In-With-X Extension */ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, vi } from "vitest"; import { SIWxPayloadSchema, parseSIWxHeader, @@ -24,6 +24,7 @@ import { signSolanaMessage, type SolanaSigner, type EVMSigner, + type EVMMessageVerifier, } from "../src/sign-in-with-x/index"; import { safeBase64Encode } from "@x402/core/utils"; import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"; @@ -217,6 +218,113 @@ describe("Sign-In-With-X Extension", () => { }); }); + describe("Smart wallet verification (evmVerifier option)", () => { + it("should use provided verifier for EVM signatures", async () => { + const mockVerifier: EVMMessageVerifier = vi.fn().mockResolvedValue(true); + const account = privateKeyToAccount(generatePrivateKey()); + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: "eip155:8453", + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, account); + + const result = await verifySIWxSignature(payload, { + evmVerifier: mockVerifier, + }); + + expect(mockVerifier).toHaveBeenCalledOnce(); + expect(mockVerifier).toHaveBeenCalledWith({ + address: expect.any(String), + message: expect.any(String), + signature: expect.any(String), + }); + expect(result.valid).toBe(true); + }); + + it("should fallback to EOA verification when no verifier provided", async () => { + const account = privateKeyToAccount(generatePrivateKey()); + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: "eip155:8453", + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, account); + + // No verifier - should still work for EOA + const result = await verifySIWxSignature(payload); + expect(result.valid).toBe(true); + expect(result.address?.toLowerCase()).toBe(account.address.toLowerCase()); + }); + + it("should return error when verifier returns false", async () => { + const mockVerifier: EVMMessageVerifier = vi.fn().mockResolvedValue(false); + const account = privateKeyToAccount(generatePrivateKey()); + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: "eip155:8453", + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, account); + + const result = await verifySIWxSignature(payload, { + evmVerifier: mockVerifier, + }); + + expect(result.valid).toBe(false); + expect(result.error).toContain("Signature verification failed"); + }); + + it("should return error when verifier throws", async () => { + const mockVerifier: EVMMessageVerifier = vi.fn().mockRejectedValue(new Error("RPC error")); + const account = privateKeyToAccount(generatePrivateKey()); + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: "eip155:8453", + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, account); + + const result = await verifySIWxSignature(payload, { + evmVerifier: mockVerifier, + }); + + expect(result.valid).toBe(false); + expect(result.error).toContain("RPC error"); + }); + + it("should not use verifier for Solana signatures", async () => { + const mockVerifier: EVMMessageVerifier = vi.fn(); + const keypair = nacl.sign.keyPair(); + const address = encodeBase58(keypair.publicKey); + + const solanaSigner: SolanaSigner = { + signMessage: async (msg: Uint8Array) => nacl.sign.detached(msg, keypair.secretKey), + publicKey: address, + }; + + const extension = declareSIWxExtension({ + resourceUri: "https://api.example.com/resource", + network: SOLANA_MAINNET, + }); + + const payload = await createSIWxPayload(extension["sign-in-with-x"].info, solanaSigner); + + const result = await verifySIWxSignature(payload, { + evmVerifier: mockVerifier, + }); + + // Verifier should NOT be called for Solana + expect(mockVerifier).not.toHaveBeenCalled(); + expect(result.valid).toBe(true); + expect(result.address).toBe(address); + }); + }); + describe("Solana constants", () => { it("should export Solana network constants", () => { expect(SOLANA_MAINNET).toBe("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"); From 19df6c0abe4cdfb5df09a9a54a594dce81b43363 Mon Sep 17 00:00:00 2001 From: sragss Date: Wed, 7 Jan 2026 15:30:33 -0500 Subject: [PATCH 15/20] add domain param; support outside EVM verifier for contract wallets; Solana CAIP-2 compliant; rm clockskew slop --- .../extensions/src/sign-in-with-x/client.ts | 2 + .../extensions/src/sign-in-with-x/declare.ts | 18 ++++++-- .../extensions/src/sign-in-with-x/index.ts | 4 +- .../extensions/src/sign-in-with-x/schema.ts | 13 +++++- .../extensions/src/sign-in-with-x/solana.ts | 24 +++------- .../extensions/src/sign-in-with-x/types.ts | 31 ++++++------- .../extensions/src/sign-in-with-x/validate.ts | 7 +-- .../extensions/test/sign-in-with-x.test.ts | 46 ++++++++++++++----- 8 files changed, 89 insertions(+), 56 deletions(-) diff --git a/typescript/packages/extensions/src/sign-in-with-x/client.ts b/typescript/packages/extensions/src/sign-in-with-x/client.ts index 8cc26e1cf..c0fee8ce7 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/client.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/client.ts @@ -54,12 +54,14 @@ export async function createSIWxPayload( uri: serverExtension.uri, version: serverExtension.version, chainId: serverExtension.chainId, + type: serverExtension.type, nonce: serverExtension.nonce, issuedAt: serverExtension.issuedAt, expirationTime: serverExtension.expirationTime, notBefore: serverExtension.notBefore, requestId: serverExtension.requestId, resources: serverExtension.resources, + signatureScheme: serverExtension.signatureScheme, signature, }; } diff --git a/typescript/packages/extensions/src/sign-in-with-x/declare.ts b/typescript/packages/extensions/src/sign-in-with-x/declare.ts index f0ef3bae1..6c806e15a 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/declare.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/declare.ts @@ -5,10 +5,20 @@ */ import { randomBytes } from "crypto"; -import type { SIWxExtension, SIWxExtensionInfo, DeclareSIWxOptions } from "./types"; +import type { SIWxExtension, SIWxExtensionInfo, DeclareSIWxOptions, SignatureType } from "./types"; import { SIGN_IN_WITH_X } from "./types"; import { buildSIWxSchema } from "./schema"; +/** + * Derive signature type from network. + * + * @param network - CAIP-2 network identifier + * @returns Signature algorithm type + */ +function getSignatureType(network: string): SignatureType { + return network.startsWith("solana:") ? "ed25519" : "eip191"; +} + /** * Create a SIWX extension declaration for PaymentRequired.extensions * @@ -24,6 +34,7 @@ import { buildSIWxSchema } from "./schema"; * @example * ```typescript * const extensions = declareSIWxExtension({ + * domain: 'api.example.com', * resourceUri: 'https://api.example.com/data', * network: 'eip155:8453', * statement: 'Sign in to access your purchased content', @@ -39,8 +50,6 @@ import { buildSIWxSchema } from "./schema"; * ``` */ export function declareSIWxExtension(options: DeclareSIWxOptions): Record { - const url = new URL(options.resourceUri); - // Auto-generate fields per spec const nonce = randomBytes(16).toString("hex"); const issuedAt = new Date().toISOString(); @@ -48,10 +57,11 @@ export function declareSIWxExtension(options: DeclareSIWxOptions): Record = { - "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": "mainnet", - EtWTRABZaYq6iMfeYKouRu166VU2xqa1: "devnet", - "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z": "testnet", -}; - -/** - * Extract network name from CAIP-2 Solana chainId. + * Extract chain reference from CAIP-2 Solana chainId. * * @param chainId - CAIP-2 format chain ID (e.g., "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp") - * @returns Human-readable network name or the reference if unknown + * @returns Chain reference (genesis hash) * * @example * ```typescript - * extractSolanaNetwork("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp") // "mainnet" - * extractSolanaNetwork("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") // "devnet" - * extractSolanaNetwork("solana:custom123") // "custom123" + * extractSolanaChainReference("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp") // "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" * ``` */ -export function extractSolanaNetwork(chainId: string): string { +export function extractSolanaChainReference(chainId: string): string { const [, reference] = chainId.split(":"); - return SOLANA_NETWORKS[reference] ?? reference; + return reference; } /** @@ -87,7 +75,7 @@ export function formatSIWSMessage(info: SIWxExtensionInfo, address: string): str lines.push( `URI: ${info.uri}`, `Version: ${info.version}`, - `Chain ID: ${extractSolanaNetwork(info.chainId)}`, + `Chain ID: ${extractSolanaChainReference(info.chainId)}`, `Nonce: ${info.nonce}`, `Issued At: ${info.issuedAt}`, ); diff --git a/typescript/packages/extensions/src/sign-in-with-x/types.ts b/typescript/packages/extensions/src/sign-in-with-x/types.ts index 9a3bdf766..871d8da76 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/types.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/types.ts @@ -25,18 +25,19 @@ export const SIGN_IN_WITH_X = "sign-in-with-x"; */ export type SignatureScheme = | "eip191" // personal_sign (default for EVM EOAs) - | "eip712" // typed data signing | "eip1271" // smart contract wallet verification | "eip6492" // counterfactual smart wallet verification - | "siws" // Sign-In-With-Solana - | "sep10"; // Stellar SEP-10 + | "siws"; // Sign-In-With-Solana + +/** Signature algorithm type per CAIP-122 */ +export type SignatureType = "eip191" | "ed25519"; /** * Server-declared extension info included in PaymentRequired.extensions * Per CHANGELOG-v2.md lines 263-272 */ export interface SIWxExtensionInfo { - /** Server's domain (derived from resourceUri host) */ + /** Server's domain */ domain: string; /** Full resource URI */ uri: string; @@ -46,6 +47,8 @@ export interface SIWxExtensionInfo { version: string; /** CAIP-2 chain identifier (e.g., "eip155:8453") */ chainId: string; + /** Signature algorithm type per CAIP-122 */ + type: SignatureType; /** Cryptographic nonce (SDK auto-generates) */ nonce: string; /** ISO 8601 timestamp (SDK auto-generates) */ @@ -58,10 +61,7 @@ export interface SIWxExtensionInfo { requestId?: string; /** Associated resources */ resources?: string[]; - /** - * Signature scheme hint (informational only). - * Verification auto-detects from chainId prefix, not this field. - */ + /** Signature scheme hint (informational) */ signatureScheme?: SignatureScheme; } @@ -79,6 +79,7 @@ export interface SIWxExtensionSchema { uri: { type: "string"; format: "uri" }; version: { type: "string" }; chainId: { type: "string" }; + type: { type: "string" }; nonce: { type: "string" }; issuedAt: { type: "string"; format: "date-time" }; expirationTime?: { type: "string"; format: "date-time" }; @@ -111,13 +112,14 @@ export const SIWxPayloadSchema = z.object({ uri: z.string(), version: z.string(), chainId: z.string(), + type: z.enum(["eip191", "ed25519"]), nonce: z.string(), issuedAt: z.string(), expirationTime: z.string().optional(), notBefore: z.string().optional(), requestId: z.string().optional(), resources: z.array(z.string()).optional(), - signatureScheme: z.enum(["eip191", "eip712", "eip1271", "eip6492", "siws", "sep10"]).optional(), + signatureScheme: z.enum(["eip191", "eip1271", "eip6492", "siws"]).optional(), signature: z.string(), }); @@ -130,7 +132,9 @@ export type SIWxPayload = z.infer; * Options for declaring SIWX extension on server */ export interface DeclareSIWxOptions { - /** Full resource URI (domain derived from this) */ + /** Server's domain (must match request origin) */ + domain: string; + /** Full resource URI */ resourceUri: string; /** Human-readable purpose */ statement?: string; @@ -140,16 +144,11 @@ export interface DeclareSIWxOptions { * CAIP-2 network identifier. * - EVM: "eip155:8453" (Base), "eip155:1" (Ethereum mainnet) * - Solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" (mainnet) - * - * Use SOLANA_MAINNET, SOLANA_DEVNET, or SOLANA_TESTNET constants for Solana. */ network: `eip155:${string}` | `solana:${string}` | (string & {}); /** Optional explicit expiration time */ expirationTime?: string; - /** - * Signature scheme hint (informational only). - * Passed to clients as UX hint but does not affect verification. - */ + /** Signature scheme hint (informational) */ signatureScheme?: SignatureScheme; } diff --git a/typescript/packages/extensions/src/sign-in-with-x/validate.ts b/typescript/packages/extensions/src/sign-in-with-x/validate.ts index 2a684924c..10e534f06 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/validate.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/validate.ts @@ -10,9 +10,6 @@ import type { SIWxPayload, SIWxValidationResult, SIWxValidationOptions } from ". /** Default maximum age for issuedAt: 5 minutes per spec */ const DEFAULT_MAX_AGE_MS = 5 * 60 * 1000; -/** Allow 1 minute clock skew for future timestamps */ -const CLOCK_SKEW_MS = 60 * 1000; - /** * Validate SIWX message fields. * @@ -85,10 +82,10 @@ export async function validateSIWxMessage( error: `Message too old: ${Math.round(age / 1000)}s exceeds ${maxAge / 1000}s limit`, }; } - if (age < -CLOCK_SKEW_MS) { + if (age < 0) { return { valid: false, - error: "issuedAt is in the future (beyond clock skew tolerance)", + error: "issuedAt is in the future", }; } diff --git a/typescript/packages/extensions/test/sign-in-with-x.test.ts b/typescript/packages/extensions/test/sign-in-with-x.test.ts index 10aeb2735..fa6a1dc96 100644 --- a/typescript/packages/extensions/test/sign-in-with-x.test.ts +++ b/typescript/packages/extensions/test/sign-in-with-x.test.ts @@ -17,7 +17,7 @@ import { formatSIWSMessage, decodeBase58, encodeBase58, - extractSolanaNetwork, + extractSolanaChainReference, verifySolanaSignature, getEVMAddress, getSolanaAddress, @@ -37,6 +37,7 @@ const validPayload = { uri: "https://api.example.com/data", version: "1", chainId: "eip155:8453", + type: "eip191" as const, nonce: "abc123def456", issuedAt: new Date().toISOString(), expirationTime: new Date(Date.now() + 5 * 60 * 1000).toISOString(), @@ -64,6 +65,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com", version: "1", chainId: "eip155:8453", + type: "eip191" as const, nonce: "abc123", issuedAt: new Date().toISOString(), signature: "0xabcdef", @@ -110,6 +112,7 @@ describe("Sign-In-With-X Extension", () => { describe("declareSIWxExtension", () => { it("should create extension with auto-generated fields", () => { const result = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/data", network: "eip155:8453", statement: "Sign in to access", @@ -120,6 +123,7 @@ describe("Sign-In-With-X Extension", () => { expect(extension.info.domain).toBe("api.example.com"); expect(extension.info.uri).toBe("https://api.example.com/data"); expect(extension.info.chainId).toBe("eip155:8453"); + expect(extension.info.type).toBe("eip191"); expect(extension.info.nonce).toBeDefined(); expect(extension.info.nonce.length).toBe(32); expect(extension.info.issuedAt).toBeDefined(); @@ -155,6 +159,7 @@ describe("Sign-In-With-X Extension", () => { statement: "Sign in to access", version: "1", chainId: "eip155:8453", + type: "eip191" as const, nonce: "abc12345def67890", issuedAt: "2024-01-01T00:00:00.000Z", resources: ["https://api.example.com"], @@ -185,6 +190,7 @@ describe("Sign-In-With-X Extension", () => { const account = privateKeyToAccount(generatePrivateKey()); const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: "eip155:8453", statement: "Sign in to access your content", @@ -206,6 +212,7 @@ describe("Sign-In-With-X Extension", () => { const account = privateKeyToAccount(generatePrivateKey()); const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: "eip155:8453", }); @@ -224,6 +231,7 @@ describe("Sign-In-With-X Extension", () => { const account = privateKeyToAccount(generatePrivateKey()); const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: "eip155:8453", }); @@ -247,6 +255,7 @@ describe("Sign-In-With-X Extension", () => { const account = privateKeyToAccount(generatePrivateKey()); const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: "eip155:8453", }); @@ -264,6 +273,7 @@ describe("Sign-In-With-X Extension", () => { const account = privateKeyToAccount(generatePrivateKey()); const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: "eip155:8453", }); @@ -283,6 +293,7 @@ describe("Sign-In-With-X Extension", () => { const account = privateKeyToAccount(generatePrivateKey()); const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: "eip155:8453", }); @@ -308,6 +319,7 @@ describe("Sign-In-With-X Extension", () => { }; const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: SOLANA_MAINNET, }); @@ -359,17 +371,17 @@ describe("Sign-In-With-X Extension", () => { }); }); - describe("extractSolanaNetwork", () => { - it("should extract mainnet", () => { - expect(extractSolanaNetwork(SOLANA_MAINNET)).toBe("mainnet"); + describe("extractSolanaChainReference", () => { + it("should extract mainnet reference", () => { + expect(extractSolanaChainReference(SOLANA_MAINNET)).toBe("5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"); }); - it("should extract devnet", () => { - expect(extractSolanaNetwork(SOLANA_DEVNET)).toBe("devnet"); + it("should extract devnet reference", () => { + expect(extractSolanaChainReference(SOLANA_DEVNET)).toBe("EtWTRABZaYq6iMfeYKouRu166VU2xqa1"); }); - it("should return reference for unknown networks", () => { - expect(extractSolanaNetwork("solana:customnetwork123")).toBe("customnetwork123"); + it("should return reference for custom networks", () => { + expect(extractSolanaChainReference("solana:customnetwork123")).toBe("customnetwork123"); }); }); @@ -381,6 +393,7 @@ describe("Sign-In-With-X Extension", () => { statement: "Sign in to access", version: "1", chainId: SOLANA_MAINNET, + type: "ed25519" as const, nonce: "abc123", issuedAt: "2024-01-01T00:00:00.000Z", resources: ["https://api.example.com/data"], @@ -390,7 +403,7 @@ describe("Sign-In-With-X Extension", () => { expect(message).toContain("wants you to sign in with your Solana account:"); expect(message).toContain("BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); - expect(message).toContain("Chain ID: mainnet"); + expect(message).toContain("Chain ID: 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"); expect(message).toContain("Nonce: abc123"); expect(message).toContain("Sign in to access"); }); @@ -401,6 +414,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com", version: "1", chainId: SOLANA_DEVNET, + type: "ed25519" as const, nonce: "xyz789", issuedAt: "2024-01-01T00:00:00.000Z", }; @@ -408,7 +422,7 @@ describe("Sign-In-With-X Extension", () => { const message = formatSIWSMessage(info, "TestAddress123"); expect(message).toContain("wants you to sign in with your Solana account:"); - expect(message).toContain("Chain ID: devnet"); + expect(message).toContain("Chain ID: EtWTRABZaYq6iMfeYKouRu166VU2xqa1"); expect(message).not.toContain("Sign in to access"); }); }); @@ -420,6 +434,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com", version: "1", chainId: "eip155:1", + type: "eip191" as const, nonce: "abc12345678", issuedAt: "2024-01-01T00:00:00.000Z", }; @@ -436,6 +451,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com", version: "1", chainId: SOLANA_MAINNET, + type: "ed25519" as const, nonce: "abc12345678", issuedAt: "2024-01-01T00:00:00.000Z", }; @@ -443,7 +459,7 @@ describe("Sign-In-With-X Extension", () => { const message = createSIWxMessage(info, "BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW"); expect(message).toContain("wants you to sign in with your Solana account:"); - expect(message).toContain("Chain ID: mainnet"); + expect(message).toContain("Chain ID: 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"); }); it("should throw for unsupported chain namespaces", () => { @@ -452,6 +468,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com", version: "1", chainId: "cosmos:cosmoshub-4", + type: "eip191" as const, nonce: "abc12345678", issuedAt: "2024-01-01T00:00:00.000Z", }; @@ -499,6 +516,7 @@ describe("Sign-In-With-X Extension", () => { const payload = { ...validPayload, chainId: "cosmos:cosmoshub-4", + type: "eip191" as const, }; const result = await verifySIWxSignature(payload); @@ -516,6 +534,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com/data", version: "1", chainId: SOLANA_MAINNET, + type: "ed25519" as const, nonce: "test123", issuedAt: new Date().toISOString(), }; @@ -543,6 +562,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com", version: "1", chainId: SOLANA_MAINNET, + type: "ed25519" as const, nonce: "test123", issuedAt: new Date().toISOString(), address: encodeBase58(new Uint8Array(32).fill(1)), // Valid 32-byte key @@ -636,6 +656,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com/data", version: "1", chainId: SOLANA_MAINNET, + type: "ed25519" as const, nonce: "test123456789", issuedAt: new Date().toISOString(), }; @@ -661,6 +682,7 @@ describe("Sign-In-With-X Extension", () => { }; const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: SOLANA_MAINNET, statement: "Sign in to access", @@ -689,6 +711,7 @@ describe("Sign-In-With-X Extension", () => { }; const extension = declareSIWxExtension({ + domain: "api.example.com", resourceUri: "https://api.example.com/resource", network: SOLANA_DEVNET, }); @@ -714,6 +737,7 @@ describe("Sign-In-With-X Extension", () => { uri: "https://api.example.com", version: "1", chainId: SOLANA_MAINNET, + type: "ed25519" as const, nonce: "test12345", issuedAt: new Date().toISOString(), signatureScheme: "eip191" as const, // Wrong hint - should be "siws" From f22abd574b261c2a568abe717d27d935d743b1b2 Mon Sep 17 00:00:00 2001 From: sragss Date: Thu, 8 Jan 2026 15:12:46 -0500 Subject: [PATCH 16/20] fix missing type field; update lock file --- examples/typescript/pnpm-lock.yaml | 239 +++++++++++++----- .../extensions/src/sign-in-with-x/verify.ts | 2 + 2 files changed, 178 insertions(+), 63 deletions(-) diff --git a/examples/typescript/pnpm-lock.yaml b/examples/typescript/pnpm-lock.yaml index d1d427954..10984c8dc 100644 --- a/examples/typescript/pnpm-lock.yaml +++ b/examples/typescript/pnpm-lock.yaml @@ -72,12 +72,24 @@ importers: ../../typescript/packages/extensions: dependencies: + '@scure/base': + specifier: ^1.2.6 + version: 1.2.6 '@x402/core': specifier: workspace:* version: link:../core ajv: specifier: ^8.17.1 version: 8.17.1 + siwe: + specifier: ^2.3.2 + version: 2.3.2(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + tweetnacl: + specifier: ^1.0.3 + version: 1.0.3 + viem: + specifier: ^2.43.5 + version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zod: specifier: ^3.24.2 version: 3.25.76 @@ -7447,6 +7459,17 @@ packages: zod: optional: true + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -10292,6 +10315,14 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + ox@0.11.1: + resolution: {integrity: sha512-1l1gOLAqg0S0xiN1dH5nkPna8PucrZgrIJOfS49MLNiMevxu07Iz4ZjuJS9N+xifvT+PsZyIptS7WHM8nC+0+A==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + ox@0.4.4: resolution: {integrity: sha512-oJPEeCDs9iNiPs6J0rTx+Y0KGeCGyCAA3zo94yZhm8G5WpOxrwUtn2Ie/Y8IyARSqqY/j9JTKA3Fc1xs1DvFnw==} peerDependencies: @@ -11978,6 +12009,14 @@ packages: typescript: optional: true + viem@2.43.5: + resolution: {integrity: sha512-QuJpuEMEPM3EreN+vX4mVY68Sci0+zDxozYfbh/WfV+SSy/Gthm74PH8XmitXdty1xY54uTCJ+/Gbbd1IiMPSA==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -13213,7 +13252,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) transitivePeerDependencies: - '@types/react' @@ -13233,7 +13272,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.1.10)(react@19.2.1)(use-sync-external-store@1.5.0(react@19.2.1)) transitivePeerDependencies: - '@types/react' @@ -13254,7 +13293,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.4.0(react@19.1.1)) transitivePeerDependencies: - '@types/react' @@ -13279,7 +13318,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.1.10)(react@19.2.1)(use-sync-external-store@1.4.0(react@19.2.1)) transitivePeerDependencies: - '@types/react' @@ -13304,7 +13343,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@4.1.13) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) zustand: 5.0.3(@types/react@19.1.10)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) transitivePeerDependencies: - '@types/react' @@ -13329,7 +13368,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@4.1.13) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) zustand: 5.0.3(@types/react@19.2.1)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) transitivePeerDependencies: - '@types/react' @@ -13459,7 +13498,7 @@ snapshots: jose: 6.0.12 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -13482,7 +13521,7 @@ snapshots: jose: 6.0.12 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -13508,7 +13547,7 @@ snapshots: ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) jose: 5.10.0 secp256k1: 5.0.1 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - debug @@ -13730,7 +13769,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.4.0(react@19.1.1)) transitivePeerDependencies: - '@types/react' @@ -13750,7 +13789,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) transitivePeerDependencies: - '@types/react' @@ -13770,7 +13809,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.1.10)(react@19.2.1)(use-sync-external-store@1.4.0(react@19.2.1)) transitivePeerDependencies: - '@types/react' @@ -13790,7 +13829,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.3(@types/react@19.1.10)(react@19.2.1)(use-sync-external-store@1.5.0(react@19.2.1)) transitivePeerDependencies: - '@types/react' @@ -13810,7 +13849,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@4.1.13) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) zustand: 5.0.3(@types/react@19.1.10)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) transitivePeerDependencies: - '@types/react' @@ -13830,7 +13869,7 @@ snapshots: idb-keyval: 6.2.1 ox: 0.6.9(typescript@5.9.2)(zod@4.1.13) preact: 10.24.2 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) zustand: 5.0.3(@types/react@19.2.1)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) transitivePeerDependencies: - '@types/react' @@ -15204,7 +15243,7 @@ snapshots: '@privy-io/api-base': 1.6.0 bs58: 5.0.0 libphonenumber-js: 1.12.13 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -16332,7 +16371,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - bufferutil - typescript @@ -16343,7 +16382,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript @@ -16354,7 +16393,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - bufferutil - typescript @@ -16367,7 +16406,7 @@ snapshots: '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.10)(react@19.1.1) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -16401,7 +16440,7 @@ snapshots: '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.10)(react@19.2.1) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -16435,7 +16474,7 @@ snapshots: '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -16469,7 +16508,7 @@ snapshots: '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -16503,7 +16542,7 @@ snapshots: '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) valtio: 1.13.2(@types/react@19.2.1)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17069,7 +17108,7 @@ snapshots: '@walletconnect/logger': 2.1.2 '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.10)(react@19.1.1) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17106,7 +17145,7 @@ snapshots: '@walletconnect/logger': 2.1.2 '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@19.1.10)(react@19.2.1) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17143,7 +17182,7 @@ snapshots: '@walletconnect/logger': 2.1.2 '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17180,7 +17219,7 @@ snapshots: '@walletconnect/logger': 2.1.2 '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17217,7 +17256,7 @@ snapshots: '@walletconnect/logger': 2.1.2 '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) valtio: 1.13.2(@types/react@19.2.1)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17270,7 +17309,7 @@ snapshots: '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.10)(react@19.1.1) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17312,7 +17351,7 @@ snapshots: '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.10)(react@19.2.1) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17354,7 +17393,7 @@ snapshots: '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17396,7 +17435,7 @@ snapshots: '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17438,7 +17477,7 @@ snapshots: '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.2.1)(react@19.2.3) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -17555,7 +17594,7 @@ snapshots: '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript @@ -17565,7 +17604,7 @@ snapshots: '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) transitivePeerDependencies: - bufferutil - typescript @@ -22044,17 +22083,27 @@ snapshots: typescript: 5.9.2 zod: 4.1.13 - abitype@1.1.0(typescript@5.9.2)(zod@3.22.4): + abitype@1.1.0(typescript@5.9.2)(zod@3.25.76): + optionalDependencies: + typescript: 5.9.2 + zod: 3.25.76 + + abitype@1.1.0(typescript@5.9.2)(zod@4.1.13): + optionalDependencies: + typescript: 5.9.2 + zod: 4.1.13 + + abitype@1.2.3(typescript@5.9.2)(zod@3.22.4): optionalDependencies: typescript: 5.9.2 zod: 3.22.4 - abitype@1.1.0(typescript@5.9.2)(zod@3.25.76): + abitype@1.2.3(typescript@5.9.2)(zod@3.25.76): optionalDependencies: typescript: 5.9.2 zod: 3.25.76 - abitype@1.1.0(typescript@5.9.2)(zod@4.1.13): + abitype@1.2.3(typescript@5.9.2)(zod@4.1.13): optionalDependencies: typescript: 5.9.2 zod: 4.1.13 @@ -25625,6 +25674,51 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + ox@0.11.1(typescript@5.9.2)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.11.0 + '@noble/ciphers': 1.3.0 + '@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.2)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - zod + + ox@0.11.1(typescript@5.9.2)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.0 + '@noble/ciphers': 1.3.0 + '@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.2)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - zod + + ox@0.11.1(typescript@5.9.2)(zod@4.1.13): + dependencies: + '@adraffy/ens-normalize': 1.11.0 + '@noble/ciphers': 1.3.0 + '@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.2)(zod@4.1.13) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - zod + ox@0.4.4(typescript@5.9.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.0 @@ -25739,21 +25833,6 @@ snapshots: transitivePeerDependencies: - zod - ox@0.9.6(typescript@5.9.2)(zod@3.22.4): - dependencies: - '@adraffy/ens-normalize': 1.11.0 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.22.4) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - zod - ox@0.9.6(typescript@5.9.2)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.0 @@ -27726,15 +27805,15 @@ snapshots: - utf-8-validate - zod - viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.22.4): + viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(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.1.0(typescript@5.9.2)(zod@3.22.4) + abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.6(typescript@5.9.2)(zod@3.22.4) + ox: 0.9.6(typescript@5.9.2)(zod@3.25.76) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.2 @@ -27743,15 +27822,15 @@ snapshots: - utf-8-validate - zod - viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.1.0(typescript@5.9.2)(zod@4.1.13) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.6(typescript@5.9.2)(zod@3.25.76) + ox: 0.9.6(typescript@5.9.2)(zod@4.1.13) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.2 @@ -27760,15 +27839,49 @@ snapshots: - utf-8-validate - zod - viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13): + viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.22.4): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@4.1.13) + abitype: 1.2.3(typescript@5.9.2)(zod@3.22.4) isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.6(typescript@5.9.2)(zod@4.1.13) + ox: 0.11.1(typescript@5.9.2)(zod@3.22.4) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(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.2)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.11.1(typescript@5.9.2)(zod@3.25.76) + ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13): + 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.2)(zod@4.1.13) + isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.11.1(typescript@5.9.2)(zod@4.1.13) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.9.2 diff --git a/typescript/packages/extensions/src/sign-in-with-x/verify.ts b/typescript/packages/extensions/src/sign-in-with-x/verify.ts index 746311c5b..34ae338f3 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/verify.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/verify.ts @@ -88,6 +88,7 @@ async function verifyEVMPayload( statement: payload.statement, version: payload.version, chainId: payload.chainId, + type: payload.type, nonce: payload.nonce, issuedAt: payload.issuedAt, expirationTime: payload.expirationTime, @@ -137,6 +138,7 @@ function verifySolanaPayload(payload: SIWxPayload): SIWxVerifyResult { statement: payload.statement, version: payload.version, chainId: payload.chainId, + type: payload.type, nonce: payload.nonce, issuedAt: payload.issuedAt, expirationTime: payload.expirationTime, From b63c2d623afd7febbbaa3eea8dd2f5a2579ba498 Mon Sep 17 00:00:00 2001 From: sragss Date: Fri, 16 Jan 2026 19:58:16 -0500 Subject: [PATCH 17/20] Add Sign-In-With-X extension spec --- specs/extensions/sign-in-with-x.md | 250 +++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 specs/extensions/sign-in-with-x.md diff --git a/specs/extensions/sign-in-with-x.md b/specs/extensions/sign-in-with-x.md new file mode 100644 index 000000000..1a206aa5a --- /dev/null +++ b/specs/extensions/sign-in-with-x.md @@ -0,0 +1,250 @@ +# Extension: `sign-in-with-x` + +## Summary + +The `sign-in-with-x` extension enables [CAIP-122](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-122.md) compliant wallet-based authentication for x402-protected resources. Clients prove control of a wallet address by signing a challenge message, allowing servers to identify returning users and skip payment for addresses that have previously paid. + +This is a **Server ↔ Client** extension. The Facilitator is not involved in the authentication flow. + +## PaymentRequired + +A Server advertises SIWX support by including the `sign-in-with-x` key in the `extensions` object of the `402 Payment Required` response. + +```json +{ + "x402Version": "2", + "accepts": [ + { + "scheme": "exact", + "network": "eip155:8453", + "amount": "10000", + "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C", + "maxTimeoutSeconds": 60, + "extra": { + "name": "USDC", + "version": "2" + } + } + ], + "extensions": { + "sign-in-with-x": { + "info": { + "domain": "api.example.com", + "uri": "https://api.example.com/premium-data", + "version": "1", + "chainId": "eip155:8453", + "type": "eip191", + "nonce": "a1b2c3d4e5f67890a1b2c3d4e5f67890", + "issuedAt": "2024-01-15T10:30:00.000Z", + "expirationTime": "2024-01-15T10:35:00.000Z", + "statement": "Sign in to access premium data", + "resources": ["https://api.example.com/premium-data"], + "signatureScheme": "eip191" + }, + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "domain": { "type": "string" }, + "address": { "type": "string" }, + "statement": { "type": "string" }, + "uri": { "type": "string", "format": "uri" }, + "version": { "type": "string" }, + "chainId": { "type": "string" }, + "type": { "type": "string" }, + "nonce": { "type": "string" }, + "issuedAt": { "type": "string", "format": "date-time" }, + "expirationTime": { "type": "string", "format": "date-time" }, + "notBefore": { "type": "string", "format": "date-time" }, + "requestId": { "type": "string" }, + "resources": { "type": "array", "items": { "type": "string", "format": "uri" } }, + "signature": { "type": "string" } + }, + "required": [ + "domain", + "address", + "uri", + "version", + "chainId", + "type", + "nonce", + "issuedAt", + "signature" + ] + } + } + } +} +``` + +## Client Request + +To authenticate, the Client signs the challenge message and sends the proof in the `SIGN-IN-WITH-X` HTTP header as base64-encoded JSON. + +```http +GET /premium-data HTTP/1.1 +Host: api.example.com +SIGN-IN-WITH-X: eyJkb21haW4iOiJhcGkuZXhhbXBsZS5jb20iLCJhZGRyZXNzIjoiMHg4NTdiMDY1MTlFOTFlM0E1NDUzODc5MWJEYmIwRTIyMzczZTM2YjY2IiwidXJpIjoiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vcHJlbWl1bS1kYXRhIiwidmVyc2lvbiI6IjEiLCJjaGFpbklkIjoiZWlwMTU1Ojg0NTMiLCJ0eXBlIjoiZWlwMTkxIiwibm9uY2UiOiJhMWIyYzNkNGU1ZjY3ODkwYTFiMmMzZDRlNWY2Nzg5MCIsImlzc3VlZEF0IjoiMjAyNC0wMS0xNVQxMDozMDowMC4wMDBaIiwiZXhwaXJhdGlvblRpbWUiOiIyMDI0LTAxLTE1VDEwOjM1OjAwLjAwMFoiLCJzdGF0ZW1lbnQiOiJTaWduIGluIHRvIGFjY2VzcyBwcmVtaXVtIGRhdGEiLCJyZXNvdXJjZXMiOlsiaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vcHJlbWl1bS1kYXRhIl0sInNpZ25hdHVyZVNjaGVtZSI6ImVpcDE5MSIsInNpZ25hdHVyZSI6IjB4MmQ2YTc1ODhkNmFjY2E1MDVjYmYwZDlhNGEyMjdlMGM1MmM2YzM0MDA4YzhlODk4NmExMjgzMjU5NzY0MTczNjA4YTJjZTY0OTY2NDJlMzc3ZDZkYThkYmJmNTgzNmU5YmQxNTA5MmY5ZWNhYjA1ZGVkM2Q2MjkzYWYxNDhiNTcxYyJ9 +``` + +The base64 header decodes to: + +```json +{ + "domain": "api.example.com", + "address": "0x857b06519E91e3A54538791bDbb0E22373e36b66", + "uri": "https://api.example.com/premium-data", + "version": "1", + "chainId": "eip155:8453", + "type": "eip191", + "nonce": "a1b2c3d4e5f67890a1b2c3d4e5f67890", + "issuedAt": "2024-01-15T10:30:00.000Z", + "expirationTime": "2024-01-15T10:35:00.000Z", + "statement": "Sign in to access premium data", + "resources": ["https://api.example.com/premium-data"], + "signatureScheme": "eip191", + "signature": "0x2d6a7588d6acca505cbf0d9a4a227e0c52c6c34008c8e8986a1283259764173608a2ce6496642e377d6da8dbbf5836e9bd15092f9ecab05ded3d6293af148b571c" +} +``` + +--- + +## Server-Declared Fields + +The Server includes these fields in `extensions["sign-in-with-x"].info`: + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `domain` | `string` | Required | Server's domain (e.g., `"api.example.com"`). MUST match the request host. | +| `uri` | `string` | Required | Full resource URI being accessed. | +| `version` | `string` | Required | CAIP-122 version. Always `"1"`. | +| `chainId` | `string` | Required | CAIP-2 chain identifier (e.g., `"eip155:8453"`). | +| `type` | `string` | Required | Signature algorithm: `"eip191"` for EVM, `"ed25519"` for Solana. | +| `nonce` | `string` | Required | Cryptographic nonce (32 hex characters). Server MUST generate this. | +| `issuedAt` | `string` | Required | ISO 8601 timestamp when challenge was created. | +| `statement` | `string` | Optional | Human-readable purpose for signing. | +| `expirationTime` | `string` | Optional | ISO 8601 timestamp when challenge expires. Default: 5 minutes from `issuedAt`. | +| `notBefore` | `string` | Optional | ISO 8601 timestamp before which the signature is not valid. | +| `requestId` | `string` | Optional | Correlation ID for the request. | +| `resources` | `string[]` | Optional | URIs associated with the request. | +| `signatureScheme` | `string` | Optional | Hint for client signing UX: `"eip191"`, `"eip1271"`, `"eip6492"`, or `"siws"`. | + +--- + +## Client Proof Fields + +The Client echoes all server fields and adds: + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `address` | `string` | Required | Wallet address that signed the message. Checksummed for EVM, Base58 for Solana. | +| `signature` | `string` | Required | Cryptographic signature. Hex-encoded (`0x...`) for EVM, Base58 for Solana. | + +--- + +## Supported Chains + +### EVM (`eip155:*`) + +- **Type**: `eip191` +- **Signature Schemes**: `eip191` (EOA), `eip1271` (smart contract wallet), `eip6492` (counterfactual wallet) +- **Message Format**: [EIP-4361 (SIWE)](https://eips.ethereum.org/EIPS/eip-4361) +- **Chain ID Examples**: `eip155:1` (Ethereum), `eip155:8453` (Base), `eip155:137` (Polygon) + +### Solana (`solana:*`) + +- **Type**: `ed25519` +- **Signature Scheme**: `siws` +- **Message Format**: [Sign-In With Solana](https://github.com/phantom/sign-in-with-solana) +- **Chain ID Examples**: `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` (mainnet), `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` (devnet) + +--- + +## Message Format + +### EVM (SIWE/EIP-4361) + +``` +api.example.com wants you to sign in with your Ethereum account: +0x857b06519E91e3A54538791bDbb0E22373e36b66 + +Sign in to access premium data + +URI: https://api.example.com/premium-data +Version: 1 +Chain ID: 8453 +Nonce: a1b2c3d4e5f67890a1b2c3d4e5f67890 +Issued At: 2024-01-15T10:30:00.000Z +Expiration Time: 2024-01-15T10:35:00.000Z +Resources: +- https://api.example.com/premium-data +``` + +### Solana (SIWS) + +``` +api.example.com wants you to sign in with your Solana account: +BSmWDgE9ex6dZYbiTsJGcwMEgFp8q4aWh92hdErQPeVW + +Sign in to access premium data + +URI: https://api.example.com/premium-data +Version: 1 +Chain ID: 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp +Nonce: a1b2c3d4e5f67890a1b2c3d4e5f67890 +Issued At: 2024-01-15T10:30:00.000Z +Expiration Time: 2024-01-15T10:35:00.000Z +Resources: +- https://api.example.com/premium-data +``` + +--- + +## Verification Logic + +When the Server receives a request with the `SIGN-IN-WITH-X` header: + +### 1. Parse Header + +Base64 decode the header value and JSON parse the result. + +### 2. Validate Message Fields + +- **Domain**: `domain` MUST match the request host exactly. +- **URI**: `uri` MUST start with the expected resource origin. +- **Issued At**: `issuedAt` MUST be recent (default: < 5 minutes) and MUST NOT be in the future. +- **Expiration**: If `expirationTime` is present, it MUST be in the future. +- **Not Before**: If `notBefore` is present, it MUST be in the past. +- **Nonce**: MUST be unique. Server SHOULD track used nonces to prevent replay attacks. + +### 3. Verify Signature + +Route verification by `chainId` prefix: + +- **`eip155:*`**: Reconstruct SIWE message, verify using ECDSA recovery (EOA) or on-chain verification (EIP-1271/EIP-6492 for smart wallets). +- **`solana:*`**: Reconstruct SIWS message, verify Ed25519 signature. + +### 4. Check Payment History + +If signature is valid, the Server checks whether the recovered `address` has previously paid for the requested resource. This is application-specific logic. + +--- + +## Security Considerations + +- **Domain Binding**: The `domain` field prevents signature reuse across different services. +- **Nonce Uniqueness**: Each challenge MUST have a unique nonce to prevent replay attacks. +- **Temporal Bounds**: The `issuedAt`, `expirationTime`, and `notBefore` fields constrain signature validity windows. +- **Chain-Specific Verification**: Signatures are verified using chain-appropriate algorithms, preventing cross-chain signature reuse. +- **Smart Wallet Support**: EIP-1271 and EIP-6492 verification requires an RPC call to the wallet contract. + +--- + +## References + +- [CAIP-122: Sign-In With X](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-122.md) +- [EIP-4361: Sign-In With Ethereum (SIWE)](https://eips.ethereum.org/EIPS/eip-4361) +- [Sign-In With Solana](https://github.com/phantom/sign-in-with-solana) +- [CAIP-2: Blockchain ID Specification](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) +- [Core x402 Specification](../x402-specification-v2.md) From 348b551aa620c73f1476ca9f39ce902d04eaaba7 Mon Sep 17 00:00:00 2001 From: sragss Date: Fri, 16 Jan 2026 20:57:01 -0500 Subject: [PATCH 18/20] server & client siwx examples --- .../clients/sign-in-with-x/.env-local | 2 + .../clients/sign-in-with-x/.prettierrc | 11 + .../clients/sign-in-with-x/eslint.config.js | 73 + .../clients/sign-in-with-x/index.ts | 127 + .../clients/sign-in-with-x/package.json | 34 + .../clients/sign-in-with-x/pnpm-lock.yaml | 2702 +++++++++++++++ .../clients/sign-in-with-x/test-logs.txt | 51 + .../clients/sign-in-with-x/tsconfig.json | 15 + examples/typescript/pnpm-lock.yaml | 626 +++- .../servers/sign-in-with-x/.env-local | 2 + .../servers/sign-in-with-x/.prettierrc | 11 + .../servers/sign-in-with-x/eslint.config.js | 72 + .../servers/sign-in-with-x/index.ts | 83 + .../servers/sign-in-with-x/package.json | 34 + .../servers/sign-in-with-x/pnpm-lock.yaml | 3033 +++++++++++++++++ .../servers/sign-in-with-x/siwx-middleware.ts | 82 + .../servers/sign-in-with-x/tsconfig.json | 15 + 17 files changed, 6921 insertions(+), 52 deletions(-) create mode 100644 examples/typescript/clients/sign-in-with-x/.env-local create mode 100644 examples/typescript/clients/sign-in-with-x/.prettierrc create mode 100644 examples/typescript/clients/sign-in-with-x/eslint.config.js create mode 100644 examples/typescript/clients/sign-in-with-x/index.ts create mode 100644 examples/typescript/clients/sign-in-with-x/package.json create mode 100644 examples/typescript/clients/sign-in-with-x/pnpm-lock.yaml create mode 100644 examples/typescript/clients/sign-in-with-x/test-logs.txt create mode 100644 examples/typescript/clients/sign-in-with-x/tsconfig.json create mode 100644 examples/typescript/servers/sign-in-with-x/.env-local create mode 100644 examples/typescript/servers/sign-in-with-x/.prettierrc create mode 100644 examples/typescript/servers/sign-in-with-x/eslint.config.js create mode 100644 examples/typescript/servers/sign-in-with-x/index.ts create mode 100644 examples/typescript/servers/sign-in-with-x/package.json create mode 100644 examples/typescript/servers/sign-in-with-x/pnpm-lock.yaml create mode 100644 examples/typescript/servers/sign-in-with-x/siwx-middleware.ts create mode 100644 examples/typescript/servers/sign-in-with-x/tsconfig.json diff --git a/examples/typescript/clients/sign-in-with-x/.env-local b/examples/typescript/clients/sign-in-with-x/.env-local new file mode 100644 index 000000000..9b315fe6e --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/.env-local @@ -0,0 +1,2 @@ +EVM_PRIVATE_KEY= +RESOURCE_SERVER_URL=http://localhost:4021 diff --git a/examples/typescript/clients/sign-in-with-x/.prettierrc b/examples/typescript/clients/sign-in-with-x/.prettierrc new file mode 100644 index 000000000..ffb416b74 --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/.prettierrc @@ -0,0 +1,11 @@ +{ + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "avoid", + "printWidth": 100, + "proseWrap": "never" +} diff --git a/examples/typescript/clients/sign-in-with-x/eslint.config.js b/examples/typescript/clients/sign-in-with-x/eslint.config.js new file mode 100644 index 000000000..bb0051d58 --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/eslint.config.js @@ -0,0 +1,73 @@ +import js from "@eslint/js"; +import ts from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; +import prettier from "eslint-plugin-prettier"; +import jsdoc from "eslint-plugin-jsdoc"; +import importPlugin from "eslint-plugin-import"; + +export default [ + { + ignores: ["dist/**", "node_modules/**"], + }, + { + files: ["**/*.ts"], + languageOptions: { + parser: tsParser, + sourceType: "module", + ecmaVersion: 2020, + globals: { + process: "readonly", + __dirname: "readonly", + module: "readonly", + require: "readonly", + Buffer: "readonly", + exports: "readonly", + setTimeout: "readonly", + clearTimeout: "readonly", + setInterval: "readonly", + clearInterval: "readonly", + fetch: "readonly", + }, + }, + plugins: { + "@typescript-eslint": ts, + prettier: prettier, + jsdoc: jsdoc, + import: importPlugin, + }, + rules: { + ...ts.configs.recommended.rules, + "import/first": "error", + "prettier/prettier": "error", + "@typescript-eslint/member-ordering": "error", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }], + "jsdoc/tag-lines": ["error", "any", { startLines: 1 }], + "jsdoc/check-alignment": "error", + "jsdoc/no-undefined-types": "off", + "jsdoc/check-param-names": "error", + "jsdoc/check-tag-names": "error", + "jsdoc/check-types": "error", + "jsdoc/implements-on-classes": "error", + "jsdoc/require-description": "error", + "jsdoc/require-jsdoc": [ + "error", + { + require: { + FunctionDeclaration: true, + MethodDefinition: true, + ClassDeclaration: true, + ArrowFunctionExpression: false, + FunctionExpression: false, + }, + }, + ], + "jsdoc/require-param": "error", + "jsdoc/require-param-description": "error", + "jsdoc/require-param-type": "off", + "jsdoc/require-returns": "error", + "jsdoc/require-returns-description": "error", + "jsdoc/require-returns-type": "off", + "jsdoc/require-hyphen-before-param-description": ["error", "always"], + }, + }, +]; diff --git a/examples/typescript/clients/sign-in-with-x/index.ts b/examples/typescript/clients/sign-in-with-x/index.ts new file mode 100644 index 000000000..dc02f45f0 --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/index.ts @@ -0,0 +1,127 @@ +import { config } from "dotenv"; +import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; +import { registerExactEvmScheme } from "@x402/evm/exact/client"; +import { privateKeyToAccount } from "viem/accounts"; +import { decodePaymentRequiredHeader } from "@x402/core/http"; +import { + createSIWxPayload, + encodeSIWxHeader, + SIGN_IN_WITH_X, + SIWxExtensionInfo, +} from "@x402/extensions/sign-in-with-x"; +config(); + +const evmPrivateKey = process.env.EVM_PRIVATE_KEY as `0x${string}`; +const baseURL = process.env.RESOURCE_SERVER_URL || "http://localhost:4021"; + +const evmSigner = privateKeyToAccount(evmPrivateKey); + +const client = new x402Client(); +registerExactEvmScheme(client, { signer: evmSigner }); +const fetchWithPayment = wrapFetchWithPayment(fetch, client); + +/** + * Makes a request using SIWX authentication (for returning users). + * + * @param url - The URL to request + * @returns The response data + */ +async function fetchWithSIWx(url: string): Promise { + // First request to get SIWX extension info from 402 response + const probeResponse = await fetch(url); + if (probeResponse.status !== 402) { + return probeResponse.json(); + } + + const paymentRequiredHeader = probeResponse.headers.get("PAYMENT-REQUIRED"); + if (!paymentRequiredHeader) { + throw new Error("Missing PAYMENT-REQUIRED header"); + } + + const paymentRequired = decodePaymentRequiredHeader(paymentRequiredHeader); + const siwxExtension = paymentRequired.extensions?.[SIGN_IN_WITH_X] as + | { info: SIWxExtensionInfo } + | undefined; + + if (!siwxExtension) { + throw new Error("Server does not support SIWX"); + } + + // Create and send SIWX proof + const payload = await createSIWxPayload(siwxExtension.info, evmSigner); + const siwxHeader = encodeSIWxHeader(payload); + + const authResponse = await fetch(url, { + headers: { [SIGN_IN_WITH_X]: siwxHeader }, + }); + + if (!authResponse.ok) { + throw new Error(`SIWX auth failed: ${authResponse.status}`); + } + + return authResponse.json(); +} + +/** + * Demonstrates the SIWX flow for a single resource. + * + * @param path - The resource path + */ +async function demonstrateResource(path: string): Promise { + const url = `${baseURL}${path}`; + console.log(`\n--- ${path} ---`); + + // First request: pay for access + console.log("1. First request (paying)..."); + const paidResponse = await fetchWithPayment(url); + console.log(" Response:", await paidResponse.json()); + + // Second request: use SIWX to prove we already paid + console.log("2. Second request (SIWX auth)..."); + const siwxResponse = await fetchWithSIWx(url); + console.log(" Response:", siwxResponse); +} + +/** + * Tests SIWX auth only (assumes server pre-seeded with TEST_ADDRESS). + * + * @param path - The resource path + */ +async function testSIWxOnly(path: string): Promise { + const url = `${baseURL}${path}`; + console.log(`\n--- Testing SIWX auth for ${path} ---`); + + console.log("1. Request without auth (expect 402)..."); + const noAuthResponse = await fetch(url); + console.log(` Status: ${noAuthResponse.status}`); + + console.log("2. Request with SIWX auth..."); + const siwxResponse = await fetchWithSIWx(url); + console.log(" Response:", siwxResponse); +} + +/** + * Main entry point demonstrating SIWX authentication flow. + */ +async function main(): Promise { + console.log(`Client address: ${evmSigner.address}`); + console.log(`Server: ${baseURL}`); + + const testOnly = process.env.TEST_SIWX_ONLY === "true"; + + if (testOnly) { + // Test mode: assumes server has TEST_ADDRESS pre-seeded + await testSIWxOnly("/weather"); + console.log("\nSIWX auth test complete."); + } else { + // Full flow: pay then auth + await demonstrateResource("/weather"); + await demonstrateResource("/joke"); + console.log("\nDone. Each resource required payment once, then SIWX auth worked."); + } +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/examples/typescript/clients/sign-in-with-x/package.json b/examples/typescript/clients/sign-in-with-x/package.json new file mode 100644 index 000000000..2cb20dc13 --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/package.json @@ -0,0 +1,34 @@ +{ + "name": "@x402/sign-in-with-x-client-example", + "private": true, + "type": "module", + "scripts": { + "start": "tsx index.ts", + "dev": "tsx index.ts", + "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"", + "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"", + "lint": "eslint . --ext .ts --fix", + "lint:check": "eslint . --ext .ts" + }, + "dependencies": { + "@x402/core": "workspace:*", + "@x402/evm": "workspace:*", + "@x402/extensions": "workspace:*", + "@x402/fetch": "workspace:*", + "dotenv": "^16.4.7", + "viem": "^2.39.0" + }, + "devDependencies": { + "@eslint/js": "^9.24.0", + "@types/node": "^22.10.5", + "@typescript-eslint/eslint-plugin": "^8.29.1", + "@typescript-eslint/parser": "^8.29.1", + "eslint": "^9.24.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.6.9", + "eslint-plugin-prettier": "^5.2.6", + "prettier": "3.5.2", + "tsx": "^4.7.0", + "typescript": "^5.3.0" + } +} diff --git a/examples/typescript/clients/sign-in-with-x/pnpm-lock.yaml b/examples/typescript/clients/sign-in-with-x/pnpm-lock.yaml new file mode 100644 index 000000000..b55f1d4a6 --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/pnpm-lock.yaml @@ -0,0 +1,2702 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@x402/core': + specifier: workspace:* + version: link:../../../../typescript/packages/core + '@x402/evm': + specifier: workspace:* + version: link:../../../../typescript/packages/mechanisms/evm + '@x402/extensions': + specifier: workspace:* + version: link:../../../../typescript/packages/extensions + '@x402/fetch': + specifier: workspace:* + version: link:../../../../typescript/packages/http/fetch + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + viem: + specifier: ^2.39.0 + version: 2.44.4(typescript@5.9.3) + devDependencies: + '@eslint/js': + specifier: ^9.24.0 + version: 9.39.2 + '@types/node': + specifier: ^22.10.5 + version: 22.19.7 + '@typescript-eslint/eslint-plugin': + specifier: ^8.29.1 + version: 8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^8.29.1 + version: 8.53.0(eslint@9.39.2)(typescript@5.9.3) + eslint: + specifier: ^9.24.0 + version: 9.39.2 + eslint-plugin-import: + specifier: ^2.31.0 + version: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2) + eslint-plugin-jsdoc: + specifier: ^50.6.9 + version: 50.8.0(eslint@9.39.2) + eslint-plugin-prettier: + specifier: ^5.2.6 + version: 5.5.5(eslint@9.39.2)(prettier@3.5.2) + prettier: + specifier: 3.5.2 + version: 3.5.2 + tsx: + specifier: ^4.7.0 + version: 4.21.0 + typescript: + specifier: ^5.3.0 + version: 5.9.3 + +packages: + + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + + '@es-joy/jsdoccomment@0.50.2': + resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} + engines: {node: '>=18'} + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@22.19.7': + resolution: {integrity: sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==} + + '@typescript-eslint/eslint-plugin@8.53.0': + resolution: {integrity: sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.53.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.53.0': + resolution: {integrity: sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.53.0': + resolution: {integrity: sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.53.0': + resolution: {integrity: sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.53.0': + resolution: {integrity: sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.53.0': + resolution: {integrity: sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.53.0': + resolution: {integrity: sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.53.0': + resolution: {integrity: sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.53.0': + resolution: {integrity: sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.53.0': + resolution: {integrity: sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsdoc@50.8.0: + resolution: {integrity: sha512-UyGb5755LMFWPrZTEqqvTJ3urLz1iqj+bYOHFNag+sw3NvaMWP9K2z+uIn37XfNALmQLQyrBlJ5mkiVPL7ADEg==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-prettier@5.5.5: + resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdoc-type-pratt-parser@4.1.0: + resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} + engines: {node: '>=12.0.0'} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + ox@0.11.3: + resolution: {integrity: sha512-1bWYGk/xZel3xro3l8WGg6eq4YEKlaqvyMtVhfMFpbJzK2F6rj4EDRtqDCWVEJMkzcmEi9uW2QxsqELokOlarw==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} + engines: {node: '>=6.0.0'} + + prettier@3.5.2: + resolution: {integrity: sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==} + engines: {node: '>=14'} + hasBin: true + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + synckit@0.11.12: + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} + engines: {node: ^14.18.0 || >=16.0.0} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + viem@2.44.4: + resolution: {integrity: sha512-sJDLVl2EsS5Fo7GSWZME5CXEV7QRYkUJPeBw7ac+4XI3D4ydvMw/gjulTsT5pgqcpu70BploFnOAC6DLpan1Yg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@adraffy/ens-normalize@1.11.1': {} + + '@es-joy/jsdoccomment@0.50.2': + dependencies: + '@types/estree': 1.0.8 + '@typescript-eslint/types': 8.53.0 + comment-parser: 1.4.1 + esquery: 1.7.0 + jsdoc-type-pratt-parser: 4.1.0 + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + dependencies: + eslint: 9.39.2 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/hashes@1.8.0': {} + + '@pkgr/core@0.2.9': {} + + '@rtsao/scc@1.1.0': {} + + '@scure/base@1.2.6': {} + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@22.19.7': + dependencies: + undici-types: 6.21.0 + + '@typescript-eslint/eslint-plugin@8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/type-utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.0 + eslint: 9.39.2 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.0 + debug: 4.4.3 + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.53.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.53.0': + dependencies: + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/visitor-keys': 8.53.0 + + '@typescript-eslint/tsconfig-utils@8.53.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.53.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.53.0': {} + + '@typescript-eslint/typescript-estree@8.53.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.53.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/visitor-keys': 8.53.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.53.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.53.0': + dependencies: + '@typescript-eslint/types': 8.53.0 + eslint-visitor-keys: 4.2.1 + + abitype@1.2.3(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + are-docs-informative@0.0.2: {} + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + comment-parser@1.4.1: {} + + concat-map@0.0.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dotenv@16.6.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + escape-string-regexp@4.0.0: {} + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.2 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsdoc@50.8.0(eslint@9.39.2): + dependencies: + '@es-joy/jsdoccomment': 0.50.2 + are-docs-informative: 0.0.2 + comment-parser: 1.4.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint: 9.39.2 + espree: 10.4.0 + esquery: 1.7.0 + parse-imports-exports: 0.2.4 + semver: 7.7.3 + spdx-expression-parse: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-plugin-prettier@5.5.5(eslint@9.39.2)(prettier@3.5.2): + dependencies: + eslint: 9.39.2 + prettier: 3.5.2 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.12 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.2: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + eventemitter3@5.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isows@1.0.7(ws@8.18.3): + dependencies: + ws: 8.18.3 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdoc-type-pratt-parser@4.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + math-intrinsics@1.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + ox@0.11.3(typescript@5.9.3): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@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) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-imports-exports@0.2.4: + dependencies: + parse-statements: 1.0.11 + + parse-statements@1.0.11: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.1: + dependencies: + fast-diff: 1.3.0 + + prettier@3.5.2: {} + + punycode@2.3.1: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + semver@6.3.1: {} + + semver@7.7.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + synckit@0.11.12: + dependencies: + '@pkgr/core': 0.2.9 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + viem@2.44.4(typescript@5.9.3): + 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) + isows: 1.0.7(ws@8.18.3) + ox: 0.11.3(typescript@5.9.3) + ws: 8.18.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + ws@8.18.3: {} + + yocto-queue@0.1.0: {} diff --git a/examples/typescript/clients/sign-in-with-x/test-logs.txt b/examples/typescript/clients/sign-in-with-x/test-logs.txt new file mode 100644 index 000000000..6a465d480 --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/test-logs.txt @@ -0,0 +1,51 @@ +================================================================================ +SIWX Example Test Logs +================================================================================ + +-------------------------------------------------------------------------------- +SERVER OUTPUT (examples/typescript/servers/sign-in-with-x/) +-------------------------------------------------------------------------------- + +> @x402/sign-in-with-x-server-example@ dev /Users/samragsdale/Documents/Code/coinbase/x402/examples/typescript/servers/sign-in-with-x +> tsx index.ts + +Server running at http://localhost:4021 +Routes: GET /weather, GET /joke +Payment recorded: 0x6d157C00d034541020fF73A989960D22b299d8e1 for /weather +Test mode: Pre-seeded payment for 0x6d157C00d034541020fF73A989960D22b299d8e1 on /weather +SIWX auth: 0x6d157C00d034541020fF73A989960D22b299d8e1 for /weather + +-------------------------------------------------------------------------------- +CLIENT OUTPUT (examples/typescript/clients/sign-in-with-x/) +-------------------------------------------------------------------------------- + +> @x402/sign-in-with-x-client-example@ dev /Users/samragsdale/Documents/Code/coinbase/x402/examples/typescript/clients/sign-in-with-x +> tsx index.ts + +Client address: 0x6d157C00d034541020fF73A989960D22b299d8e1 +Server: http://localhost:4021 + +--- Testing SIWX auth for /weather --- +1. Request without auth (expect 402)... + Status: 402 +2. Request with SIWX auth... + Response: { weather: 'sunny', temperature: 72 } + +SIWX auth test complete. + +================================================================================ +Test Configuration +================================================================================ + +Server environment variables: + EVM_ADDRESS=0x6d157C00d034541020fF73A989960D22b299d8e1 + FACILITATOR_URL=https://x402.org/facilitator + TEST_ADDRESS=0x6d157C00d034541020fF73A989960D22b299d8e1 + +Client environment variables: + EVM_PRIVATE_KEY=0x08d1901112ed174fb2832786b926fd926370da1040d0f0b92608bfab953449ba + TEST_SIWX_ONLY=true + +Note: Full payment flow (demonstrateResource) cannot be tested locally because +the x402.org facilitator rejects localhost URLs. The SIWX auth flow is tested +using the TEST_ADDRESS pre-seeding mechanism. diff --git a/examples/typescript/clients/sign-in-with-x/tsconfig.json b/examples/typescript/clients/sign-in-with-x/tsconfig.json new file mode 100644 index 000000000..78f9479b1 --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "bundler", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "strict": true, + "resolveJsonModule": true, + "baseUrl": ".", + "types": ["node"] + }, + "include": ["index.ts"] +} diff --git a/examples/typescript/pnpm-lock.yaml b/examples/typescript/pnpm-lock.yaml index 10984c8dc..d12d90c53 100644 --- a/examples/typescript/pnpm-lock.yaml +++ b/examples/typescript/pnpm-lock.yaml @@ -1440,6 +1440,58 @@ importers: specifier: ^5.3.0 version: 5.9.2 + clients/sign-in-with-x: + dependencies: + '@x402/core': + specifier: workspace:* + version: link:../../../../typescript/packages/core + '@x402/evm': + specifier: workspace:* + version: link:../../../../typescript/packages/mechanisms/evm + '@x402/extensions': + specifier: workspace:* + version: link:../../../../typescript/packages/extensions + '@x402/fetch': + specifier: workspace:* + version: link:../../../../typescript/packages/http/fetch + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + viem: + specifier: ^2.39.0 + version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + devDependencies: + '@eslint/js': + specifier: ^9.24.0 + version: 9.33.0 + '@typescript-eslint/eslint-plugin': + specifier: ^8.29.1 + version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2) + '@typescript-eslint/parser': + specifier: ^8.29.1 + version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2) + eslint: + specifier: ^9.24.0 + version: 9.33.0(jiti@2.6.1) + eslint-plugin-import: + specifier: ^2.31.0 + version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1)) + eslint-plugin-jsdoc: + specifier: ^50.6.9 + version: 50.8.0(eslint@9.33.0(jiti@2.6.1)) + eslint-plugin-prettier: + specifier: ^5.2.6 + version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2) + prettier: + specifier: 3.5.2 + version: 3.5.2 + tsx: + specifier: ^4.7.0 + version: 4.20.4 + typescript: + specifier: ^5.3.0 + version: 5.9.2 + facilitator: dependencies: '@scure/base': @@ -2822,6 +2874,61 @@ importers: specifier: ^5.3.0 version: 5.9.2 + servers/sign-in-with-x: + dependencies: + '@x402/core': + specifier: workspace:* + version: link:../../../../typescript/packages/core + '@x402/evm': + specifier: workspace:* + version: link:../../../../typescript/packages/mechanisms/evm + '@x402/express': + specifier: workspace:* + version: link:../../../../typescript/packages/http/express + '@x402/extensions': + specifier: workspace:* + version: link:../../../../typescript/packages/extensions + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + express: + specifier: ^4.18.2 + version: 4.21.2 + devDependencies: + '@eslint/js': + specifier: ^9.24.0 + version: 9.33.0 + '@types/express': + specifier: ^5.0.1 + version: 5.0.3 + '@typescript-eslint/eslint-plugin': + specifier: ^8.29.1 + version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2) + '@typescript-eslint/parser': + specifier: ^8.29.1 + version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2) + eslint: + specifier: ^9.24.0 + version: 9.33.0(jiti@2.6.1) + eslint-plugin-import: + specifier: ^2.31.0 + version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1)) + eslint-plugin-jsdoc: + specifier: ^50.6.9 + version: 50.8.0(eslint@9.33.0(jiti@2.6.1)) + eslint-plugin-prettier: + specifier: ^5.2.6 + version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2) + prettier: + specifier: 3.5.2 + version: 3.5.2 + tsx: + specifier: ^4.7.0 + version: 4.20.4 + typescript: + specifier: ^5.3.0 + version: 5.9.2 + packages: 7zip-bin@5.2.0: @@ -12443,9 +12550,9 @@ snapshots: 7zip-bin@5.2.0: {} - '@across-protocol/app-sdk@0.2.3(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@across-protocol/app-sdk@0.2.3(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@adraffy/ens-normalize@1.10.1': {} @@ -13399,11 +13506,11 @@ snapshots: '@coinbase/agentkit@0.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)': dependencies: - '@across-protocol/app-sdk': 0.2.3(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@across-protocol/app-sdk': 0.2.3(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) '@alloralabs/allora-sdk': 0.1.1 '@coinbase/coinbase-sdk': 0.20.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@jup-ag/api': 6.0.44 - '@privy-io/server-auth': 1.31.1(bufferutil@4.0.9)(encoding@0.1.13)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@privy-io/server-auth': 1.31.1(bufferutil@4.0.9)(encoding@0.1.13)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.4(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10) bs58: 4.0.1 @@ -13414,7 +13521,7 @@ snapshots: opensea-js: 7.2.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) reflect-metadata: 0.2.2 twitter-api-v2: 1.25.0 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -13435,7 +13542,7 @@ snapshots: '@coinbase/cdp-api-client': 0.0.18 jose: 6.0.12 ox: 0.8.1(typescript@5.9.2)(zod@3.25.76) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.8(@types/react@19.2.1)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)) transitivePeerDependencies: - '@types/react' @@ -13476,7 +13583,7 @@ snapshots: jose: 6.0.12 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - bufferutil @@ -13535,7 +13642,7 @@ snapshots: '@coinbase/coinbase-sdk@0.20.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@scure/bip32': 1.7.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.2)(zod@3.25.76) axios: 1.13.2 axios-mock-adapter: 1.22.0(axios@1.13.2) axios-retry: 4.5.0(axios@1.13.2) @@ -13558,9 +13665,9 @@ snapshots: '@coinbase/onchainkit@0.38.14(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)': dependencies: '@farcaster/frame-sdk': 0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) - '@farcaster/frame-wagmi-connector': 0.0.42(@farcaster/frame-sdk@0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@farcaster/frame-wagmi-connector': 0.0.42(@farcaster/frame-sdk@0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) '@tanstack/react-query': 5.90.11(react@19.2.3) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) clsx: 2.1.1 graphql: 16.11.0 graphql-request: 6.1.0(encoding@0.1.13)(graphql@16.11.0) @@ -13568,8 +13675,8 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) tailwind-merge: 2.6.0 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) - wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -13610,9 +13717,9 @@ snapshots: '@coinbase/onchainkit@0.38.19(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)': dependencies: '@farcaster/frame-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) '@tanstack/react-query': 5.90.11(react@19.2.1) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) clsx: 2.1.1 graphql: 16.11.0 graphql-request: 6.1.0(encoding@0.1.13)(graphql@16.11.0) @@ -13620,8 +13727,8 @@ snapshots: react: 19.2.1 react-dom: 19.2.1(react@19.2.1) tailwind-merge: 2.6.0 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -13663,9 +13770,9 @@ snapshots: '@coinbase/onchainkit@0.38.19(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)': dependencies: '@farcaster/frame-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) - '@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) '@tanstack/react-query': 5.90.11(react@19.2.3) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) clsx: 2.1.1 graphql: 16.11.0 graphql-request: 6.1.0(encoding@0.1.13)(graphql@16.11.0) @@ -13673,8 +13780,8 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) tailwind-merge: 2.6.0 - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) - wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -14348,11 +14455,11 @@ snapshots: - utf-8-validate - zod - '@farcaster/frame-wagmi-connector@0.0.42(@farcaster/frame-sdk@0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': + '@farcaster/frame-wagmi-connector@0.0.42(@farcaster/frame-sdk@0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': dependencies: '@farcaster/frame-sdk': 0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) '@farcaster/miniapp-core@0.3.8(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)': dependencies: @@ -14438,17 +14545,17 @@ snapshots: '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) - '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: '@farcaster/miniapp-sdk': 0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': + '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': dependencies: '@farcaster/miniapp-sdk': 0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) '@farcaster/quick-auth@0.0.5(typescript@5.9.2)': dependencies: @@ -14530,6 +14637,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@gemini-wallet/core@0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + '@metamask/rpc-errors': 7.0.2 + eventemitter3: 5.0.1 + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@gemini-wallet/core@0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': + dependencies: + '@metamask/rpc-errors': 7.0.2 + eventemitter3: 5.0.1 + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + transitivePeerDependencies: + - supports-color + '@graphql-typed-document-node/core@3.2.0(graphql@16.11.0)': dependencies: graphql: 16.11.0 @@ -15250,7 +15373,7 @@ snapshots: - typescript - utf-8-validate - '@privy-io/server-auth@1.31.1(bufferutil@4.0.9)(encoding@0.1.13)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@privy-io/server-auth@1.31.1(bufferutil@4.0.9)(encoding@0.1.13)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: '@hpke/chacha20poly1305': 1.7.1 '@hpke/core': 1.7.4 @@ -15269,7 +15392,7 @@ snapshots: type-fest: 3.13.1 optionalDependencies: ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - encoding @@ -20344,6 +20467,59 @@ snapshots: - utf-8-validate - zod + '@wagmi/connectors@6.2.0(10b5cfe7782601b7906d6575627c234c)': + dependencies: + '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + cbw-sdk: '@coinbase/wallet-sdk@3.9.3' + porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/react-query' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - react-native + - supports-color + - uploadthing + - use-sync-external-store + - utf-8-validate + - wagmi + - ws + - zod + '@wagmi/connectors@6.2.0(23b94b54ae454356a8c069b1a59b1f3b)': dependencies: '@base-org/account': 2.4.0(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) @@ -20503,6 +20679,59 @@ snapshots: - ws - zod + '@wagmi/connectors@6.2.0(7a6a533243fd625b190c4338193cc7eb)': + dependencies: + '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13) + '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + cbw-sdk: '@coinbase/wallet-sdk@3.9.3' + porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/react-query' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - react-native + - supports-color + - uploadthing + - use-sync-external-store + - utf-8-validate + - wagmi + - ws + - zod + '@wagmi/connectors@6.2.0(7c0fb5ad6e91c192e500875cd884eb4c)': dependencies: '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) @@ -20564,7 +20793,7 @@ snapshots: '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)) @@ -20609,15 +20838,68 @@ snapshots: - ws - zod - '@wagmi/core@2.19.0(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)': dependencies: - eventemitter3: 5.0.1 - mipd: 0.0.7(typescript@5.9.2) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - zustand: 5.0.0(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) - optionalDependencies: - '@tanstack/query-core': 5.90.11 - typescript: 5.9.2 + '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13) + '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + cbw-sdk: '@coinbase/wallet-sdk@3.9.3' + porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/react-query' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react + - react-native + - supports-color + - uploadthing + - use-sync-external-store + - utf-8-validate + - wagmi + - ws + - zod + + '@wagmi/core@2.19.0(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.2) + viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.0(@types/react@19.1.10)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) + optionalDependencies: + '@tanstack/query-core': 5.90.11 + typescript: 5.9.2 transitivePeerDependencies: - '@types/react' - immer @@ -20669,11 +20951,26 @@ snapshots: - react - use-sync-external-store - '@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.9.2) - viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + zustand: 5.0.0(@types/react@19.1.10)(react@19.2.1)(use-sync-external-store@1.4.0(react@19.2.1)) + optionalDependencies: + '@tanstack/query-core': 5.90.11 + typescript: 5.9.2 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.2) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) zustand: 5.0.0(@types/react@19.1.10)(react@19.2.1)(use-sync-external-store@1.5.0(react@19.2.1)) optionalDependencies: '@tanstack/query-core': 5.90.11 @@ -20699,6 +20996,21 @@ snapshots: - react - use-sync-external-store + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.2) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + zustand: 5.0.0(@types/react@19.1.10)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) + optionalDependencies: + '@tanstack/query-core': 5.90.11 + typescript: 5.9.2 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': dependencies: eventemitter3: 5.0.1 @@ -20714,6 +21026,21 @@ snapshots: - react - use-sync-external-store + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.2) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + zustand: 5.0.0(@types/react@19.1.10)(react@19.2.3)(use-sync-external-store@1.5.0(react@19.2.3)) + optionalDependencies: + '@tanstack/query-core': 5.90.11 + typescript: 5.9.2 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + '@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))': dependencies: eventemitter3: 5.0.1 @@ -25726,7 +26053,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.2)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25740,7 +26067,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@4.1.13) + abitype: 1.2.3(typescript@5.9.2)(zod@4.1.13) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25754,7 +26081,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.2)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25768,7 +26095,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@4.1.13) + abitype: 1.2.3(typescript@5.9.2)(zod@4.1.13) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25782,7 +26109,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.2)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25796,7 +26123,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@4.1.13) + abitype: 1.2.3(typescript@5.9.2)(zod@4.1.13) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25811,7 +26138,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.2)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25826,7 +26153,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@4.1.13) + abitype: 1.2.3(typescript@5.9.2)(zod@4.1.13) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25841,7 +26168,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.2.3(typescript@5.9.2)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -25856,7 +26183,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@4.1.13) + abitype: 1.2.3(typescript@5.9.2)(zod@4.1.13) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.2 @@ -26063,6 +26390,26 @@ snapshots: - immer - use-sync-external-store + porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): + dependencies: + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + hono: 4.10.7 + idb-keyval: 6.2.2 + mipd: 0.0.7(typescript@5.9.2) + ox: 0.9.17(typescript@5.9.2)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + zod: 4.1.13 + zustand: 5.0.8(@types/react@19.1.10)(react@19.2.1)(use-sync-external-store@1.4.0(react@19.2.1)) + optionalDependencies: + '@tanstack/react-query': 5.90.11(react@19.2.1) + react: 19.2.1 + typescript: 5.9.2 + wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + transitivePeerDependencies: + - '@types/react' + - immer + - use-sync-external-store + porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)): dependencies: '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) @@ -26083,9 +26430,29 @@ snapshots: - immer - use-sync-external-store + porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)): + dependencies: + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + hono: 4.10.7 + idb-keyval: 6.2.2 + mipd: 0.0.7(typescript@5.9.2) + ox: 0.9.17(typescript@5.9.2)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + zod: 4.1.13 + zustand: 5.0.8(@types/react@19.1.10)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) + optionalDependencies: + '@tanstack/react-query': 5.90.11(react@19.2.3) + react: 19.2.3 + typescript: 5.9.2 + wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + transitivePeerDependencies: + - '@types/react' + - immer + - use-sync-external-store + porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) hono: 4.10.7 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.9.2) @@ -26103,6 +26470,26 @@ snapshots: - immer - use-sync-external-store + porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)): + dependencies: + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + hono: 4.10.7 + idb-keyval: 6.2.2 + mipd: 0.0.7(typescript@5.9.2) + ox: 0.9.17(typescript@5.9.2)(zod@4.1.13) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + zod: 4.1.13 + zustand: 5.0.8(@types/react@19.1.10)(react@19.2.3)(use-sync-external-store@1.4.0(react@19.2.3)) + optionalDependencies: + '@tanstack/react-query': 5.90.11(react@19.2.3) + react: 19.2.3 + typescript: 5.9.2 + wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + transitivePeerDependencies: + - '@types/react' + - immer + - use-sync-external-store + porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)): dependencies: '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) @@ -28091,6 +28478,51 @@ snapshots: - ws - zod + wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): + dependencies: + '@tanstack/react-query': 5.90.11(react@19.2.1) + '@wagmi/connectors': 6.2.0(10b5cfe7782601b7906d6575627c234c) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + react: 19.2.1 + use-sync-external-store: 1.4.0(react@19.2.1) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/query-core' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react-native + - supports-color + - uploadthing + - utf-8-validate + - ws + - zod + wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): dependencies: '@tanstack/react-query': 5.90.11(react@19.2.3) @@ -28136,6 +28568,51 @@ snapshots: - ws - zod + wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): + dependencies: + '@tanstack/react-query': 5.90.11(react@19.2.3) + '@wagmi/connectors': 6.2.0(7a6a533243fd625b190c4338193cc7eb) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + react: 19.2.3 + use-sync-external-store: 1.4.0(react@19.2.3) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/query-core' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react-native + - supports-color + - uploadthing + - utf-8-validate + - ws + - zod + wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): dependencies: '@tanstack/react-query': 5.90.11(react@19.2.3) @@ -28181,6 +28658,51 @@ snapshots: - ws - zod + wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): + dependencies: + '@tanstack/react-query': 5.90.11(react@19.2.3) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)) + react: 19.2.3 + use-sync-external-store: 1.4.0(react@19.2.3) + viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@react-native-async-storage/async-storage' + - '@tanstack/query-core' + - '@types/react' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/kv' + - aws4fetch + - bufferutil + - db0 + - debug + - encoding + - expo-auth-session + - expo-crypto + - expo-web-browser + - fastestsmallesttextencoderdecoder + - immer + - ioredis + - react-native + - supports-color + - uploadthing + - utf-8-validate + - ws + - zod + wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13): dependencies: '@tanstack/react-query': 5.90.11(react@19.2.3) diff --git a/examples/typescript/servers/sign-in-with-x/.env-local b/examples/typescript/servers/sign-in-with-x/.env-local new file mode 100644 index 000000000..7c000b6d8 --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/.env-local @@ -0,0 +1,2 @@ +EVM_ADDRESS= +FACILITATOR_URL=https://x402.org/facilitator diff --git a/examples/typescript/servers/sign-in-with-x/.prettierrc b/examples/typescript/servers/sign-in-with-x/.prettierrc new file mode 100644 index 000000000..ffb416b74 --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/.prettierrc @@ -0,0 +1,11 @@ +{ + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "avoid", + "printWidth": 100, + "proseWrap": "never" +} diff --git a/examples/typescript/servers/sign-in-with-x/eslint.config.js b/examples/typescript/servers/sign-in-with-x/eslint.config.js new file mode 100644 index 000000000..ca28b5c47 --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/eslint.config.js @@ -0,0 +1,72 @@ +import js from "@eslint/js"; +import ts from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; +import prettier from "eslint-plugin-prettier"; +import jsdoc from "eslint-plugin-jsdoc"; +import importPlugin from "eslint-plugin-import"; + +export default [ + { + ignores: ["dist/**", "node_modules/**"], + }, + { + files: ["**/*.ts"], + languageOptions: { + parser: tsParser, + sourceType: "module", + ecmaVersion: 2020, + globals: { + process: "readonly", + __dirname: "readonly", + module: "readonly", + require: "readonly", + Buffer: "readonly", + exports: "readonly", + setTimeout: "readonly", + clearTimeout: "readonly", + setInterval: "readonly", + clearInterval: "readonly", + }, + }, + plugins: { + "@typescript-eslint": ts, + prettier: prettier, + jsdoc: jsdoc, + import: importPlugin, + }, + rules: { + ...ts.configs.recommended.rules, + "import/first": "error", + "prettier/prettier": "error", + "@typescript-eslint/member-ordering": "error", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }], + "jsdoc/tag-lines": ["error", "any", { startLines: 1 }], + "jsdoc/check-alignment": "error", + "jsdoc/no-undefined-types": "off", + "jsdoc/check-param-names": "error", + "jsdoc/check-tag-names": "error", + "jsdoc/check-types": "error", + "jsdoc/implements-on-classes": "error", + "jsdoc/require-description": "error", + "jsdoc/require-jsdoc": [ + "error", + { + require: { + FunctionDeclaration: true, + MethodDefinition: true, + ClassDeclaration: true, + ArrowFunctionExpression: false, + FunctionExpression: false, + }, + }, + ], + "jsdoc/require-param": "error", + "jsdoc/require-param-description": "error", + "jsdoc/require-param-type": "off", + "jsdoc/require-returns": "error", + "jsdoc/require-returns-description": "error", + "jsdoc/require-returns-type": "off", + "jsdoc/require-hyphen-before-param-description": ["error", "always"], + }, + }, +]; diff --git a/examples/typescript/servers/sign-in-with-x/index.ts b/examples/typescript/servers/sign-in-with-x/index.ts new file mode 100644 index 000000000..7120db5f0 --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/index.ts @@ -0,0 +1,83 @@ +import { config } from "dotenv"; +import express from "express"; +import { paymentMiddleware, x402ResourceServer } from "@x402/express"; +import { ExactEvmScheme } from "@x402/evm/exact/server"; +import { HTTPFacilitatorClient } from "@x402/core/server"; +import { declareSIWxExtension } from "@x402/extensions/sign-in-with-x"; +import { createSIWxMiddleware, recordPayment } from "./siwx-middleware"; +config(); + +const evmAddress = process.env.EVM_ADDRESS as `0x${string}`; +if (!evmAddress) { + console.error("Missing EVM_ADDRESS"); + process.exit(1); +} + +const facilitatorUrl = process.env.FACILITATOR_URL; +if (!facilitatorUrl) { + console.error("Missing FACILITATOR_URL"); + process.exit(1); +} + +const PORT = 4021; +const HOST = `localhost:${PORT}`; +const NETWORK = "eip155:84532" as const; + +const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl }); + +const resourceServer = new x402ResourceServer(facilitatorClient) + .register(NETWORK, new ExactEvmScheme()) + .onAfterSettle(async ctx => { + const payload = ctx.paymentPayload.payload as { authorization: { from: string } }; + const address = payload.authorization.from; + const resource = new URL(ctx.paymentPayload.resource.url).pathname; + recordPayment(resource, address); + }); + +/** + * Creates route config with SIWX extension. + * + * @param path - The route path + * @returns Route configuration object + */ +function routeConfig(path: string) { + return { + accepts: [{ scheme: "exact", price: "$0.001", network: NETWORK, payTo: evmAddress }], + description: `Protected resource: ${path}`, + mimeType: "application/json", + extensions: declareSIWxExtension({ + domain: HOST, + resourceUri: `http://${HOST}${path}`, + network: NETWORK, + }), + }; +} + +const routes = { + "GET /weather": routeConfig("/weather"), + "GET /joke": routeConfig("/joke"), +}; + +const app = express(); +app.use(createSIWxMiddleware(HOST)); + +// Payment middleware - skipped for SIWX-authenticated users +const payment = paymentMiddleware(routes, resourceServer); +app.use((req, res, next) => (res.locals.siwxAuthenticated ? next() : payment(req, res, next))); + +app.get("/weather", (req, res) => res.json({ weather: "sunny", temperature: 72 })); +app.get("/joke", (req, res) => + res.json({ joke: "Why do programmers prefer dark mode? Because light attracts bugs." }), +); + +app.listen(PORT, () => { + console.log(`Server running at http://localhost:${PORT}`); + console.log(`Routes: GET /weather, GET /joke`); + + // For testing: pre-seed a payment if TEST_ADDRESS is set + const testAddress = process.env.TEST_ADDRESS; + if (testAddress) { + recordPayment("/weather", testAddress); + console.log(`Test mode: Pre-seeded payment for ${testAddress} on /weather`); + } +}); diff --git a/examples/typescript/servers/sign-in-with-x/package.json b/examples/typescript/servers/sign-in-with-x/package.json new file mode 100644 index 000000000..09d6e6beb --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/package.json @@ -0,0 +1,34 @@ +{ + "name": "@x402/sign-in-with-x-server-example", + "private": true, + "type": "module", + "scripts": { + "dev": "tsx index.ts", + "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"", + "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"", + "lint": "eslint . --ext .ts --fix", + "lint:check": "eslint . --ext .ts" + }, + "dependencies": { + "dotenv": "^16.4.7", + "express": "^4.18.2", + "@x402/core": "workspace:*", + "@x402/express": "workspace:*", + "@x402/evm": "workspace:*", + "@x402/extensions": "workspace:*" + }, + "devDependencies": { + "@eslint/js": "^9.24.0", + "@types/express": "5.0.3", + "@types/node": "^22.10.5", + "@typescript-eslint/eslint-plugin": "^8.29.1", + "@typescript-eslint/parser": "^8.29.1", + "eslint": "^9.24.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.6.9", + "eslint-plugin-prettier": "^5.2.6", + "prettier": "3.5.2", + "tsx": "^4.7.0", + "typescript": "^5.3.0" + } +} diff --git a/examples/typescript/servers/sign-in-with-x/pnpm-lock.yaml b/examples/typescript/servers/sign-in-with-x/pnpm-lock.yaml new file mode 100644 index 000000000..f35a7d5d3 --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/pnpm-lock.yaml @@ -0,0 +1,3033 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@x402/core': + specifier: workspace:* + version: link:../../../../typescript/packages/core + '@x402/evm': + specifier: workspace:* + version: link:../../../../typescript/packages/mechanisms/evm + '@x402/express': + specifier: workspace:* + version: link:../../../../typescript/packages/http/express + '@x402/extensions': + specifier: workspace:* + version: link:../../../../typescript/packages/extensions + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + express: + specifier: ^4.18.2 + version: 4.22.1 + devDependencies: + '@eslint/js': + specifier: ^9.24.0 + version: 9.39.2 + '@types/express': + specifier: 5.0.3 + version: 5.0.3 + '@types/node': + specifier: ^22.10.5 + version: 22.19.7 + '@typescript-eslint/eslint-plugin': + specifier: ^8.29.1 + version: 8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^8.29.1 + version: 8.53.0(eslint@9.39.2)(typescript@5.9.3) + eslint: + specifier: ^9.24.0 + version: 9.39.2 + eslint-plugin-import: + specifier: ^2.31.0 + version: 2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2) + eslint-plugin-jsdoc: + specifier: ^50.6.9 + version: 50.8.0(eslint@9.39.2) + eslint-plugin-prettier: + specifier: ^5.2.6 + version: 5.5.5(eslint@9.39.2)(prettier@3.5.2) + prettier: + specifier: 3.5.2 + version: 3.5.2 + tsx: + specifier: ^4.7.0 + version: 4.21.0 + typescript: + specifier: ^5.3.0 + version: 5.9.3 + +packages: + + '@es-joy/jsdoccomment@0.50.2': + resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} + engines: {node: '>=18'} + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/express-serve-static-core@5.1.1': + resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} + + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@22.19.7': + resolution: {integrity: sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + + '@typescript-eslint/eslint-plugin@8.53.0': + resolution: {integrity: sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.53.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.53.0': + resolution: {integrity: sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.53.0': + resolution: {integrity: sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.53.0': + resolution: {integrity: sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.53.0': + resolution: {integrity: sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.53.0': + resolution: {integrity: sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.53.0': + resolution: {integrity: sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.53.0': + resolution: {integrity: sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.53.0': + resolution: {integrity: sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.53.0': + resolution: {integrity: sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsdoc@50.8.0: + resolution: {integrity: sha512-UyGb5755LMFWPrZTEqqvTJ3urLz1iqj+bYOHFNag+sw3NvaMWP9K2z+uIn37XfNALmQLQyrBlJ5mkiVPL7ADEg==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-prettier@5.5.5: + resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdoc-type-pratt-parser@4.1.0: + resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} + engines: {node: '>=12.0.0'} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} + engines: {node: '>=6.0.0'} + + prettier@3.5.2: + resolution: {integrity: sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==} + engines: {node: '>=14'} + hasBin: true + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.1: + resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + synckit@0.11.12: + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} + engines: {node: ^14.18.0 || >=16.0.0} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@es-joy/jsdoccomment@0.50.2': + dependencies: + '@types/estree': 1.0.8 + '@typescript-eslint/types': 8.53.0 + comment-parser: 1.4.1 + esquery: 1.7.0 + jsdoc-type-pratt-parser: 4.1.0 + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + dependencies: + eslint: 9.39.2 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@pkgr/core@0.2.9': {} + + '@rtsao/scc@1.1.0': {} + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.19.7 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.19.7 + + '@types/estree@1.0.8': {} + + '@types/express-serve-static-core@5.1.1': + dependencies: + '@types/node': 22.19.7 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@5.0.3': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.1.1 + '@types/serve-static': 2.2.0 + + '@types/http-errors@2.0.5': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@22.19.7': + dependencies: + undici-types: 6.21.0 + + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/send@1.2.1': + dependencies: + '@types/node': 22.19.7 + + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.19.7 + + '@typescript-eslint/eslint-plugin@8.53.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/type-utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.0 + eslint: 9.39.2 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.53.0 + debug: 4.4.3 + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.53.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.53.0': + dependencies: + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/visitor-keys': 8.53.0 + + '@typescript-eslint/tsconfig-utils@8.53.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.53.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.53.0': {} + + '@typescript-eslint/typescript-estree@8.53.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.53.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3) + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/visitor-keys': 8.53.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.53.0(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.53.0 + '@typescript-eslint/types': 8.53.0 + '@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.53.0': + dependencies: + '@typescript-eslint/types': 8.53.0 + eslint-visitor-keys: 4.2.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + are-docs-informative@0.0.2: {} + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-flatten@1.1.1: {} + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + balanced-match@1.0.2: {} + + body-parser@1.20.4: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.1 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + comment-parser@1.4.1: {} + + concat-map@0.0.1: {} + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.0.7: {} + + cookie@0.7.2: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + depd@2.0.0: {} + + destroy@1.2.0: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dotenv@16.6.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + es-abstract@1.24.1: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.2 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.53.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.53.0(eslint@9.39.2)(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsdoc@50.8.0(eslint@9.39.2): + dependencies: + '@es-joy/jsdoccomment': 0.50.2 + are-docs-informative: 0.0.2 + comment-parser: 1.4.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint: 9.39.2 + espree: 10.4.0 + esquery: 1.7.0 + parse-imports-exports: 0.2.4 + semver: 7.7.3 + spdx-expression-parse: 4.0.0 + transitivePeerDependencies: + - supports-color + + eslint-plugin-prettier@5.5.5(eslint@9.39.2)(prettier@3.5.2): + dependencies: + eslint: 9.39.2 + prettier: 3.5.2 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.12 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.2: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.14.1 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + ipaddr.js@1.9.1: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdoc-type-pratt-parser@4.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + math-intrinsics@1.1.0: {} + + media-typer@0.3.0: {} + + merge-descriptors@1.0.3: {} + + methods@1.1.2: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-imports-exports@0.2.4: + dependencies: + parse-statements: 1.0.11 + + parse-statements@1.0.11: {} + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@0.1.12: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.1: + dependencies: + fast-diff: 1.3.0 + + prettier@3.5.2: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + punycode@2.3.1: {} + + qs@6.14.1: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@2.5.3: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + semver@6.3.1: {} + + semver@7.7.3: {} + + send@0.19.2: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@1.16.3: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + + statuses@2.0.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.1 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + synckit@0.11.12: + dependencies: + '@pkgr/core': 0.2.9 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + toidentifier@1.0.1: {} + + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + unpipe@1.0.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + utils-merge@1.0.1: {} + + vary@1.1.2: {} + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} diff --git a/examples/typescript/servers/sign-in-with-x/siwx-middleware.ts b/examples/typescript/servers/sign-in-with-x/siwx-middleware.ts new file mode 100644 index 000000000..2225923c1 --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/siwx-middleware.ts @@ -0,0 +1,82 @@ +import { Request, Response, NextFunction } from "express"; +import { + SIGN_IN_WITH_X, + parseSIWxHeader, + validateSIWxMessage, + verifySIWxSignature, +} from "@x402/extensions/sign-in-with-x"; + +// Tracks which addresses have paid for which resources +const paidAddresses = new Map>(); + +/** + * Records that an address has paid for a resource. + * + * @param resource - The resource path + * @param address - The payer's address + */ +export function recordPayment(resource: string, address: string): void { + if (!paidAddresses.has(resource)) { + paidAddresses.set(resource, new Set()); + } + paidAddresses.get(resource)!.add(address.toLowerCase()); + console.log(`Payment recorded: ${address} for ${resource}`); +} + +/** + * Checks if an address has paid for a resource. + * + * @param resource - The resource path + * @param address - The address to check + * @returns True if the address has paid + */ +export function hasPaid(resource: string, address: string): boolean { + return paidAddresses.get(resource)?.has(address.toLowerCase()) ?? false; +} + +/** + * Creates SIWX middleware that validates proofs and skips payment for returning users. + * + * @param host - The server host (e.g., "localhost:4021") + * @returns Express middleware function + */ +export function createSIWxMiddleware(host: string) { + return async function siwxMiddleware( + req: Request, + res: Response, + next: NextFunction, + ): Promise { + const header = req.headers[SIGN_IN_WITH_X.toLowerCase()] as string | undefined; + if (!header) { + next(); + return; + } + + try { + const payload = parseSIWxHeader(header); + const resourceUri = `http://${host}${req.path}`; + + const validation = await validateSIWxMessage(payload, resourceUri); + if (!validation.valid) { + next(); + return; + } + + const verification = await verifySIWxSignature(payload); + if (!verification.valid || !verification.address) { + next(); + return; + } + + if (hasPaid(req.path, verification.address)) { + console.log(`SIWX auth: ${verification.address} for ${req.path}`); + res.locals.siwxAddress = verification.address; + res.locals.siwxAuthenticated = true; + } + } catch { + // Invalid SIWX, fall through to payment + } + + next(); + }; +} diff --git a/examples/typescript/servers/sign-in-with-x/tsconfig.json b/examples/typescript/servers/sign-in-with-x/tsconfig.json new file mode 100644 index 000000000..6b6c5fa7d --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "bundler", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "strict": true, + "resolveJsonModule": true, + "baseUrl": ".", + "types": ["node"] + }, + "include": ["index.ts", "siwx-middleware.ts"] +} From 1c6acba6a9b435f56edf1e1571511e27d60f262b Mon Sep 17 00:00:00 2001 From: sragss Date: Fri, 16 Jan 2026 21:01:23 -0500 Subject: [PATCH 19/20] chore: update pnpm-lock.yaml for sign-in-with-x examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update lockfile to include @types/node added to client example. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- examples/typescript/pnpm-lock.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/typescript/pnpm-lock.yaml b/examples/typescript/pnpm-lock.yaml index d12d90c53..728dd095f 100644 --- a/examples/typescript/pnpm-lock.yaml +++ b/examples/typescript/pnpm-lock.yaml @@ -1464,6 +1464,9 @@ importers: '@eslint/js': specifier: ^9.24.0 version: 9.33.0 + '@types/node': + specifier: ^22.10.5 + version: 22.17.2 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2) @@ -2899,8 +2902,11 @@ importers: specifier: ^9.24.0 version: 9.33.0 '@types/express': - specifier: ^5.0.1 + specifier: 5.0.3 version: 5.0.3 + '@types/node': + specifier: ^22.10.5 + version: 22.17.2 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2) From dd4f0df85c0d2a63c623ccdff9b3003d22710505 Mon Sep 17 00:00:00 2001 From: sragss Date: Sat, 17 Jan 2026 11:36:59 -0500 Subject: [PATCH 20/20] Add wrapFetchWithSIWx helper to @x402/extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a fetch wrapper for SIWX authentication that mirrors the wrapFetchWithPayment pattern from @x402/fetch. The wrapper automatically handles 402 responses with SIWX extension info. Changes: - Add fetch.ts with wrapFetchWithSIWx function - Export wrapFetchWithSIWx from sign-in-with-x index - Add DOM lib to tsconfig for Request/Response types - Simplify client example to use the new wrapper - Add .prettierignore and eslint globals to match other examples 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../clients/sign-in-with-x/.prettierignore | 8 ++ .../clients/sign-in-with-x/eslint.config.js | 1 + .../clients/sign-in-with-x/index.ts | 56 ++----------- .../servers/sign-in-with-x/.prettierignore | 8 ++ .../servers/sign-in-with-x/eslint.config.js | 1 + .../extensions/src/sign-in-with-x/fetch.ts | 81 +++++++++++++++++++ .../extensions/src/sign-in-with-x/index.ts | 1 + typescript/packages/extensions/tsconfig.json | 2 +- 8 files changed, 107 insertions(+), 51 deletions(-) create mode 100644 examples/typescript/clients/sign-in-with-x/.prettierignore create mode 100644 examples/typescript/servers/sign-in-with-x/.prettierignore create mode 100644 typescript/packages/extensions/src/sign-in-with-x/fetch.ts diff --git a/examples/typescript/clients/sign-in-with-x/.prettierignore b/examples/typescript/clients/sign-in-with-x/.prettierignore new file mode 100644 index 000000000..3049672b5 --- /dev/null +++ b/examples/typescript/clients/sign-in-with-x/.prettierignore @@ -0,0 +1,8 @@ +docs/ +dist/ +node_modules/ +coverage/ +.github/ +src/client +**/**/*.json +*.md diff --git a/examples/typescript/clients/sign-in-with-x/eslint.config.js b/examples/typescript/clients/sign-in-with-x/eslint.config.js index bb0051d58..6380771d4 100644 --- a/examples/typescript/clients/sign-in-with-x/eslint.config.js +++ b/examples/typescript/clients/sign-in-with-x/eslint.config.js @@ -21,6 +21,7 @@ export default [ module: "readonly", require: "readonly", Buffer: "readonly", + console: "readonly", exports: "readonly", setTimeout: "readonly", clearTimeout: "readonly", diff --git a/examples/typescript/clients/sign-in-with-x/index.ts b/examples/typescript/clients/sign-in-with-x/index.ts index dc02f45f0..e049e438d 100644 --- a/examples/typescript/clients/sign-in-with-x/index.ts +++ b/examples/typescript/clients/sign-in-with-x/index.ts @@ -2,13 +2,7 @@ import { config } from "dotenv"; import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; import { registerExactEvmScheme } from "@x402/evm/exact/client"; import { privateKeyToAccount } from "viem/accounts"; -import { decodePaymentRequiredHeader } from "@x402/core/http"; -import { - createSIWxPayload, - encodeSIWxHeader, - SIGN_IN_WITH_X, - SIWxExtensionInfo, -} from "@x402/extensions/sign-in-with-x"; +import { wrapFetchWithSIWx } from "@x402/extensions/sign-in-with-x"; config(); const evmPrivateKey = process.env.EVM_PRIVATE_KEY as `0x${string}`; @@ -16,51 +10,13 @@ const baseURL = process.env.RESOURCE_SERVER_URL || "http://localhost:4021"; const evmSigner = privateKeyToAccount(evmPrivateKey); +// Payment wrapper - handles initial 402 by paying const client = new x402Client(); registerExactEvmScheme(client, { signer: evmSigner }); const fetchWithPayment = wrapFetchWithPayment(fetch, client); -/** - * Makes a request using SIWX authentication (for returning users). - * - * @param url - The URL to request - * @returns The response data - */ -async function fetchWithSIWx(url: string): Promise { - // First request to get SIWX extension info from 402 response - const probeResponse = await fetch(url); - if (probeResponse.status !== 402) { - return probeResponse.json(); - } - - const paymentRequiredHeader = probeResponse.headers.get("PAYMENT-REQUIRED"); - if (!paymentRequiredHeader) { - throw new Error("Missing PAYMENT-REQUIRED header"); - } - - const paymentRequired = decodePaymentRequiredHeader(paymentRequiredHeader); - const siwxExtension = paymentRequired.extensions?.[SIGN_IN_WITH_X] as - | { info: SIWxExtensionInfo } - | undefined; - - if (!siwxExtension) { - throw new Error("Server does not support SIWX"); - } - - // Create and send SIWX proof - const payload = await createSIWxPayload(siwxExtension.info, evmSigner); - const siwxHeader = encodeSIWxHeader(payload); - - const authResponse = await fetch(url, { - headers: { [SIGN_IN_WITH_X]: siwxHeader }, - }); - - if (!authResponse.ok) { - throw new Error(`SIWX auth failed: ${authResponse.status}`); - } - - return authResponse.json(); -} +// SIWX wrapper - handles 402 by proving wallet ownership (for returning users) +const fetchWithSIWx = wrapFetchWithSIWx(fetch, evmSigner); /** * Demonstrates the SIWX flow for a single resource. @@ -79,7 +35,7 @@ async function demonstrateResource(path: string): Promise { // Second request: use SIWX to prove we already paid console.log("2. Second request (SIWX auth)..."); const siwxResponse = await fetchWithSIWx(url); - console.log(" Response:", siwxResponse); + console.log(" Response:", await siwxResponse.json()); } /** @@ -97,7 +53,7 @@ async function testSIWxOnly(path: string): Promise { console.log("2. Request with SIWX auth..."); const siwxResponse = await fetchWithSIWx(url); - console.log(" Response:", siwxResponse); + console.log(" Response:", await siwxResponse.json()); } /** diff --git a/examples/typescript/servers/sign-in-with-x/.prettierignore b/examples/typescript/servers/sign-in-with-x/.prettierignore new file mode 100644 index 000000000..3049672b5 --- /dev/null +++ b/examples/typescript/servers/sign-in-with-x/.prettierignore @@ -0,0 +1,8 @@ +docs/ +dist/ +node_modules/ +coverage/ +.github/ +src/client +**/**/*.json +*.md diff --git a/examples/typescript/servers/sign-in-with-x/eslint.config.js b/examples/typescript/servers/sign-in-with-x/eslint.config.js index ca28b5c47..e2fde7b3b 100644 --- a/examples/typescript/servers/sign-in-with-x/eslint.config.js +++ b/examples/typescript/servers/sign-in-with-x/eslint.config.js @@ -21,6 +21,7 @@ export default [ module: "readonly", require: "readonly", Buffer: "readonly", + console: "readonly", exports: "readonly", setTimeout: "readonly", clearTimeout: "readonly", diff --git a/typescript/packages/extensions/src/sign-in-with-x/fetch.ts b/typescript/packages/extensions/src/sign-in-with-x/fetch.ts new file mode 100644 index 000000000..6cbb260e6 --- /dev/null +++ b/typescript/packages/extensions/src/sign-in-with-x/fetch.ts @@ -0,0 +1,81 @@ +/** + * Fetch wrapper for SIWX authentication. + * + * Provides a convenient wrapper around fetch that automatically handles + * SIWX authentication when a 402 response includes SIWX extension info. + */ + +import { decodePaymentRequiredHeader } from "@x402/core/http"; +import type { SIWxSigner } from "./sign"; +import type { SIWxExtensionInfo } from "./types"; +import { SIGN_IN_WITH_X } from "./types"; +import { createSIWxPayload } from "./client"; +import { encodeSIWxHeader } from "./encode"; + +/** + * Wraps fetch to automatically handle SIWX authentication. + * + * When a 402 response is received with a SIWX extension: + * 1. Extracts SIWX info from PAYMENT-REQUIRED header + * 2. Creates signed SIWX proof using the provided signer + * 3. Retries the request with the SIWX header + * + * If the 402 response doesn't include SIWX extension info, the original + * response is returned unchanged (allowing payment handling to proceed). + * + * @param fetch - The fetch function to wrap (typically globalThis.fetch) + * @param signer - Wallet signer (EVMSigner or SolanaSigner) + * @returns A wrapped fetch function that handles SIWX authentication + * + * @example + * ```typescript + * import { wrapFetchWithSIWx } from '@x402/extensions/sign-in-with-x'; + * import { privateKeyToAccount } from 'viem/accounts'; + * + * const signer = privateKeyToAccount(privateKey); + * const fetchWithSIWx = wrapFetchWithSIWx(fetch, signer); + * + * // Request that may require SIWX auth (for returning paid users) + * const response = await fetchWithSIWx('https://api.example.com/data'); + * ``` + */ +export function wrapFetchWithSIWx(fetch: typeof globalThis.fetch, signer: SIWxSigner) { + return async (input: RequestInfo | URL, init?: RequestInit): Promise => { + const request = new Request(input, init); + const clonedRequest = request.clone(); + + const response = await fetch(request); + + if (response.status !== 402) { + return response; + } + + // Extract SIWX info from 402 response + const paymentRequiredHeader = response.headers.get("PAYMENT-REQUIRED"); + if (!paymentRequiredHeader) { + return response; // No PAYMENT-REQUIRED header, return original response + } + + const paymentRequired = decodePaymentRequiredHeader(paymentRequiredHeader); + const siwxExtension = paymentRequired.extensions?.[SIGN_IN_WITH_X] as + | { info: SIWxExtensionInfo } + | undefined; + + if (!siwxExtension?.info) { + return response; // Server doesn't support SIWX, return original 402 + } + + // Prevent infinite loops + if (clonedRequest.headers.has(SIGN_IN_WITH_X)) { + throw new Error("SIWX authentication already attempted"); + } + + // Create and send SIWX proof + const payload = await createSIWxPayload(siwxExtension.info, signer); + const siwxHeader = encodeSIWxHeader(payload); + + clonedRequest.headers.set(SIGN_IN_WITH_X, siwxHeader); + + return fetch(clonedRequest); + }; +} diff --git a/typescript/packages/extensions/src/sign-in-with-x/index.ts b/typescript/packages/extensions/src/sign-in-with-x/index.ts index 39eb8cd99..5e472a23b 100644 --- a/typescript/packages/extensions/src/sign-in-with-x/index.ts +++ b/typescript/packages/extensions/src/sign-in-with-x/index.ts @@ -99,6 +99,7 @@ export { buildSIWxSchema } from "./schema"; export { createSIWxMessage } from "./message"; export { createSIWxPayload } from "./client"; export { encodeSIWxHeader } from "./encode"; +export { wrapFetchWithSIWx } from "./fetch"; export { getEVMAddress, getSolanaAddress, diff --git a/typescript/packages/extensions/tsconfig.json b/typescript/packages/extensions/tsconfig.json index 667e30eed..b96e5cefa 100644 --- a/typescript/packages/extensions/tsconfig.json +++ b/typescript/packages/extensions/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "lib": ["ES2020"], + "lib": ["ES2020", "DOM"], "allowJs": false, "checkJs": false },