@wtflabs/x402-detector 0.0.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/proxy.ts","../../src/detector.ts","../../src/cache.ts"],"sourcesContent":["import type { PresetTokenConfig } from \"./types\";\n\n/**\n * EIP-3009 方法签名\n * - transferWithAuthorization(address,address,uint256,uint256,uint256,bytes32,uint8,bytes32,bytes32)\n *\n * 支持多个方法签名变体,以兼容不同的实现:\n * - 0xe3ee160e: 标准 EIP-3009 实现\n * - 0xcf092995: 某些代币的替代实现\n */\nexport const EIP3009_SIGNATURES = [\"0xe3ee160e\", \"0xcf092995\"] as const;\n\n/**\n * EIP-2612 Permit 方法签名\n * - permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\n */\nexport const EIP2612_PERMIT = \"0xd505accf\" as const;\n\n/**\n * Uniswap Permit2 合约地址(所有链相同)\n */\nexport const PERMIT2_ADDRESS = \"0x000000000022D473030F116dDEE9F6B43aC78BA3\" as const;\n\n/**\n * EIP-1967 标准实现槽位\n * keccak256(\"eip1967.proxy.implementation\") - 1\n */\nexport const EIP1967_IMPLEMENTATION_SLOT =\n \"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc\" as const;\n\n/**\n * EIP-1822 UUPS 实现槽位\n * keccak256(\"PROXIABLE\")\n */\nexport const EIP1822_IMPLEMENTATION_SLOT =\n \"0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3\" as const;\n\n/**\n * 预设 Token 配置\n * 用于已知的特殊 Token,避免重复检测\n */\nexport const PRESET_TOKEN_CAPABILITIES: Record<string, PresetTokenConfig> = {\n // World Liberty Financial USD - 只支持 permit\n \"0x8d0d000ee44948fc98c9b98a4fa4921476f08b0d\": {\n supportedMethods: [\"permit\"],\n supportedNetworks: [56], // BSC\n description: \"World Liberty Financial USD (WLFI)\",\n },\n // 可以继续添加更多预设代币\n // 示例:\n // \"0x其他代币地址\": {\n // supportedMethods: [\"eip3009\", \"permit\"],\n // supportedNetworks: [1, 56],\n // description: \"代币名称\",\n // },\n};\n\n","import type { Address, PublicClient } from \"viem\";\nimport type { Logger } from \"./types\";\nimport { EIP1967_IMPLEMENTATION_SLOT, EIP1822_IMPLEMENTATION_SLOT } from \"./constants\";\n\n/**\n * 默认 logger\n */\nconst defaultLogger: Logger = {\n log: (message: string) => console.log(message),\n error: (message: string, error?: unknown) => console.error(message, error),\n};\n\n/**\n * 检测合约是否是代理合约,并获取实现合约地址\n *\n * @param client - viem PublicClient\n * @param proxyAddress - 代理合约地址\n * @param logger - 可选的 logger\n * @returns 实现合约地址或 null\n */\nexport async function getImplementationAddress(\n client: PublicClient,\n proxyAddress: Address,\n logger: Logger | null = defaultLogger,\n): Promise<Address | null> {\n try {\n // 方法1: 尝试读取 EIP-1967 存储槽位\n try {\n const implSlotData = await client.getStorageAt({\n address: proxyAddress,\n slot: EIP1967_IMPLEMENTATION_SLOT,\n });\n if (\n implSlotData &&\n implSlotData !== \"0x0000000000000000000000000000000000000000000000000000000000000000\"\n ) {\n // 从存储槽中提取地址(最后20字节)\n const implAddress = `0x${implSlotData.slice(-40)}` as Address;\n if (implAddress !== \"0x0000000000000000000000000000000000000000\") {\n logger?.log(` 📦 Detected EIP-1967 proxy, implementation: ${implAddress}`);\n return implAddress;\n }\n }\n } catch {\n // 继续尝试其他方法\n }\n\n // 方法2: 尝试读取 EIP-1822 存储槽位\n try {\n const uupsSlotData = await client.getStorageAt({\n address: proxyAddress,\n slot: EIP1822_IMPLEMENTATION_SLOT,\n });\n if (\n uupsSlotData &&\n uupsSlotData !== \"0x0000000000000000000000000000000000000000000000000000000000000000\"\n ) {\n const implAddress = `0x${uupsSlotData.slice(-40)}` as Address;\n if (implAddress !== \"0x0000000000000000000000000000000000000000\") {\n logger?.log(` 📦 Detected EIP-1822 UUPS proxy, implementation: ${implAddress}`);\n return implAddress;\n }\n }\n } catch {\n // 继续尝试其他方法\n }\n\n // 方法3: 尝试调用 implementation() 函数\n try {\n const implABI = [\n {\n inputs: [],\n name: \"implementation\",\n outputs: [{ name: \"\", type: \"address\" }],\n stateMutability: \"view\",\n type: \"function\",\n },\n ] as const;\n\n const implAddress = (await client.readContract({\n address: proxyAddress,\n abi: implABI,\n functionName: \"implementation\",\n })) as Address;\n\n if (implAddress && implAddress !== \"0x0000000000000000000000000000000000000000\") {\n logger?.log(` 📦 Detected proxy via implementation(), implementation: ${implAddress}`);\n return implAddress;\n }\n } catch {\n // 不是代理合约或不支持 implementation() 函数\n }\n\n return null;\n } catch (error) {\n logger?.error(\"Error detecting proxy implementation:\", error);\n return null;\n }\n}\n","import type { Address, PublicClient } from \"viem\";\nimport type { PaymentMethod, TokenPaymentCapabilities, TokenInfo, Logger } from \"./types\";\nimport {\n EIP3009_SIGNATURES,\n EIP2612_PERMIT,\n PERMIT2_ADDRESS,\n PRESET_TOKEN_CAPABILITIES,\n} from \"./constants\";\nimport { getImplementationAddress } from \"./proxy\";\n\n/**\n * 默认 logger\n */\nconst defaultLogger: Logger = {\n log: (message: string) => console.log(message),\n error: (message: string, error?: unknown) => console.error(message, error),\n};\n\n/**\n * 检查合约是否支持某个方法(通过字节码检查)\n * 支持代理合约检测\n *\n * @param client - viem PublicClient\n * @param tokenAddress - 合约地址\n * @param methodSelector - 方法选择器\n * @param logger - 可选的 logger\n * @returns true 如果合约支持该方法,否则 false\n */\nasync function hasMethod(\n client: PublicClient,\n tokenAddress: Address,\n methodSelector: string,\n logger: Logger | null = defaultLogger,\n): Promise<boolean> {\n try {\n // 尝试获取合约代码\n const code = await client.getCode({ address: tokenAddress });\n if (!code) return false;\n\n // 检查字节码中是否包含方法选择器\n const hasMethodInProxy = code.toLowerCase().includes(methodSelector.slice(2).toLowerCase());\n\n // 如果代理合约中找到了方法,直接返回 true\n if (hasMethodInProxy) {\n return true;\n }\n\n // 如果代理合约中没有找到,尝试检测是否是代理合约\n const implAddress = await getImplementationAddress(client, tokenAddress, logger);\n if (implAddress) {\n // 获取实现合约的字节码\n const implCode = await client.getCode({ address: implAddress });\n if (implCode) {\n const hasMethodInImpl = implCode\n .toLowerCase()\n .includes(methodSelector.slice(2).toLowerCase());\n if (hasMethodInImpl) {\n logger?.log(` ✅ Method ${methodSelector} found in implementation contract`);\n }\n return hasMethodInImpl;\n }\n }\n\n return false;\n } catch (error) {\n logger?.error(`Error checking method ${methodSelector}:`, error);\n return false;\n }\n}\n\n/**\n * 检查合约是否支持多个方法签名中的任意一个\n * 支持代理合约检测\n *\n * @param client - viem PublicClient\n * @param tokenAddress - 合约地址\n * @param methodSelectors - 方法选择器列表\n * @param logger - 可选的 logger\n * @returns true 如果合约支持任意一个方法签名,否则 false\n */\nasync function hasAnyMethod(\n client: PublicClient,\n tokenAddress: Address,\n methodSelectors: readonly string[],\n logger: Logger | null = defaultLogger,\n): Promise<boolean> {\n try {\n // 尝试获取合约代码\n const code = await client.getCode({ address: tokenAddress });\n if (!code) return false;\n\n const codeLower = code.toLowerCase();\n\n // 检查代理合约中是否包含任何一个方法选择器\n const hasMethodInProxy = methodSelectors.some(selector =>\n codeLower.includes(selector.slice(2).toLowerCase()),\n );\n\n // 如果代理合约中找到了方法,直接返回 true\n if (hasMethodInProxy) {\n return true;\n }\n\n // 如果代理合约中没有找到,尝试检测是否是代理合约\n const implAddress = await getImplementationAddress(client, tokenAddress, logger);\n if (implAddress) {\n // 获取实现合约的字节码\n const implCode = await client.getCode({ address: implAddress });\n if (implCode) {\n const implCodeLower = implCode.toLowerCase();\n const hasMethodInImpl = methodSelectors.some(selector =>\n implCodeLower.includes(selector.slice(2).toLowerCase()),\n );\n if (hasMethodInImpl) {\n logger?.log(` ✅ Method(s) found in implementation contract`);\n }\n return hasMethodInImpl;\n }\n }\n\n return false;\n } catch (error) {\n logger?.error(`Error checking methods ${methodSelectors.join(\", \")}:`, error);\n return false;\n }\n}\n\n/**\n * 检查 Permit2 合约是否在该链上部署\n *\n * @param client - viem PublicClient\n * @param logger - 可选的 logger\n * @returns true 如果 Permit2 合约已部署,否则 false\n */\nasync function checkPermit2Support(\n client: PublicClient,\n logger: Logger | null = defaultLogger,\n): Promise<boolean> {\n try {\n // 检查 Permit2 合约是否在该链上部署\n const permit2Code = await client.getCode({ address: PERMIT2_ADDRESS });\n if (!permit2Code) return false;\n\n // 如果 Permit2 存在,理论上任何 ERC-20 都可以使用它\n return true;\n } catch (error) {\n logger?.error(\"Error checking Permit2 support:\", error);\n return false;\n }\n}\n\n/**\n * 检测 Token 支持的支付方式\n *\n * @param tokenAddress - Token 地址\n * @param client - viem PublicClient\n * @param logger - 可选的 logger\n * @returns 检测结果\n */\nexport async function detectTokenPaymentMethods(\n tokenAddress: string,\n client: PublicClient,\n logger: Logger | null = defaultLogger,\n): Promise<TokenPaymentCapabilities> {\n const address = tokenAddress.toLowerCase() as Address;\n const chainId = await client.getChainId();\n\n // 检查预设配置\n const presetCapabilities = PRESET_TOKEN_CAPABILITIES[address];\n if (presetCapabilities) {\n if (!chainId || !presetCapabilities.supportedNetworks.includes(chainId)) {\n return {\n address,\n supportedMethods: [],\n details: {\n hasEIP3009: false,\n hasPermit: false,\n hasPermit2Approval: false,\n },\n };\n }\n\n // 从预设的方法列表构建 details\n const hasEIP3009 = presetCapabilities.supportedMethods.includes(\"eip3009\");\n const hasPermit = presetCapabilities.supportedMethods.includes(\"permit\");\n const hasPermit2Approval =\n presetCapabilities.supportedMethods.includes(\"permit2\") ||\n presetCapabilities.supportedMethods.includes(\"permit2-witness\");\n\n if (hasEIP3009) {\n logger?.log(\" ✅ EIP-3009 (transferWithAuthorization) - from preset\");\n }\n if (hasPermit) {\n logger?.log(\" ✅ EIP-2612 (permit) - from preset\");\n }\n if (hasPermit2Approval) {\n logger?.log(\" ✅ Permit2 support - from preset\");\n }\n\n return {\n address,\n supportedMethods: presetCapabilities.supportedMethods,\n details: {\n hasEIP3009,\n hasPermit,\n hasPermit2Approval,\n },\n };\n }\n\n logger?.log(`🔍 Detecting payment methods for token ${address}...`);\n\n // 并行检测所有方法\n const [hasEIP3009, hasPermit, hasPermit2Approval] = await Promise.all([\n hasAnyMethod(client, address, EIP3009_SIGNATURES, logger),\n hasMethod(client, address, EIP2612_PERMIT, logger),\n checkPermit2Support(client, logger),\n ]);\n\n // 构建支持的方法列表\n const supportedMethods: PaymentMethod[] = [];\n\n if (hasEIP3009) {\n supportedMethods.push(\"eip3009\");\n logger?.log(\" ✅ EIP-3009 (transferWithAuthorization) detected\");\n }\n\n if (hasPermit) {\n supportedMethods.push(\"permit\");\n logger?.log(\" ✅ EIP-2612 (permit) detected\");\n }\n\n if (hasPermit2Approval) {\n supportedMethods.push(\"permit2\");\n supportedMethods.push(\"permit2-witness\");\n logger?.log(\" ✅ Permit2 support available (universal)\");\n }\n\n if (supportedMethods.length === 0) {\n logger?.log(\" ⚠️ No advanced payment methods detected (standard ERC-20 only)\");\n }\n\n return {\n address,\n supportedMethods,\n details: {\n hasEIP3009,\n hasPermit,\n hasPermit2Approval,\n },\n };\n}\n\n/**\n * 获取推荐的支付方式(仅返回 schema 支持的类型)\n * 按优先级排序:eip3009 > permit > permit2\n * 注意:permit2-witness 会被映射为 permit2,因为它们在 schema 中是同一种支付类型\n *\n * @param tokenAddress - Token 地址\n * @param client - viem PublicClient\n * @param logger - 可选的 logger\n * @returns 推荐的支付方式\n */\nexport async function getRecommendedPaymentMethod(\n tokenAddress: string,\n client: PublicClient,\n logger: Logger | null = defaultLogger,\n): Promise<\"eip3009\" | \"permit2\" | \"permit\" | null> {\n const capabilities = await detectTokenPaymentMethods(tokenAddress, client, logger);\n const { supportedMethods } = capabilities;\n\n if (supportedMethods.includes(\"eip3009\")) return \"eip3009\";\n if (supportedMethods.includes(\"permit\")) return \"permit\";\n // permit2 和 permit2-witness 都映射为 permit2(schema 只支持 permit2)\n if (supportedMethods.includes(\"permit2\") || supportedMethods.includes(\"permit2-witness\")) {\n return \"permit2\";\n }\n\n return null;\n}\n\n/**\n * 获取 Token 的 name 和 version 信息(用于 EIP-712 签名)\n * 支持代理合约(会自动从代理合约读取,因为代理合约会 delegatecall 到实现合约)\n *\n * @param tokenAddress - Token 地址\n * @param client - viem PublicClient\n * @param logger - 可选的 logger\n * @returns Token 的 name 和 version\n */\nexport async function getTokenInfo(\n tokenAddress: string,\n client: PublicClient,\n logger: Logger | null = defaultLogger,\n): Promise<TokenInfo> {\n const address = tokenAddress.toLowerCase() as Address;\n\n // ERC-20 标准 ABI\n const erc20ABI = [\n {\n inputs: [],\n name: \"name\",\n outputs: [{ name: \"\", type: \"string\" }],\n stateMutability: \"view\",\n type: \"function\",\n },\n ] as const;\n\n // EIP-5267 eip712Domain ABI(OpenZeppelin v5+)\n const eip712DomainABI = [\n {\n inputs: [],\n name: \"eip712Domain\",\n outputs: [\n { name: \"fields\", type: \"bytes1\" },\n { name: \"name\", type: \"string\" },\n { name: \"version\", type: \"string\" },\n { name: \"chainId\", type: \"uint256\" },\n { name: \"verifyingContract\", type: \"address\" },\n { name: \"salt\", type: \"bytes32\" },\n { name: \"extensions\", type: \"uint256[]\" },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n ] as const;\n\n // version() ABI(OpenZeppelin v4)\n const versionABI = [\n {\n inputs: [],\n name: \"version\",\n outputs: [{ name: \"\", type: \"string\" }],\n stateMutability: \"view\",\n type: \"function\",\n },\n ] as const;\n\n try {\n // 检测是否为代理合约\n const implAddress = await getImplementationAddress(client, address, logger);\n if (implAddress) {\n logger?.log(\n ` 📦 Reading token info from proxy, actual calls will be delegated to implementation`,\n );\n }\n\n // 获取 token name (对于代理合约,delegatecall 会自动转发到实现合约)\n const name = await client.readContract({\n address,\n abi: erc20ABI,\n functionName: \"name\",\n });\n\n // 尝试获取 version,优先使用 EIP-5267\n let version = \"1\"; // 默认版本\n try {\n const result = await client.readContract({\n address,\n abi: eip712DomainABI,\n functionName: \"eip712Domain\",\n });\n // eip712Domain 返回 [fields, name, version, chainId, verifyingContract, salt, extensions]\n version = result[2] as string; // version 是第 3 个元素(索引 2)\n } catch {\n // 回退到 version() 函数(OpenZeppelin v4)\n try {\n version = await client.readContract({\n address,\n abi: versionABI,\n functionName: \"version\",\n });\n } catch {\n // 如果两种方法都不可用,使用默认值 \"1\"\n logger?.log(` ℹ️ Using default version \"1\" for token ${address}`);\n }\n }\n\n return {\n name: name as string,\n version: version as string,\n };\n } catch (error) {\n logger?.error(`Error getting token info for ${address}:`, error);\n throw new Error(`Failed to get token info: ${error}`);\n }\n}\n","import type { PublicClient } from \"viem\";\nimport type { TokenDetectionResult, TokenDetectorOptions, Logger } from \"./types\";\nimport { detectTokenPaymentMethods, getTokenInfo } from \"./detector\";\n\n/**\n * 默认 logger\n */\nconst defaultLogger: Logger = {\n log: (message: string) => console.log(message),\n error: (message: string, error?: unknown) => console.error(message, error),\n};\n\n/**\n * Token 检测器 - 带缓存功能的 SDK\n *\n * 主要用于 x402-server,也可以独立使用\n */\nexport class TokenDetector {\n /** 缓存存储 (chainId:address -> TokenDetectionResult) */\n private cache: Map<string, TokenDetectionResult> = new Map();\n\n /** viem PublicClient */\n private client: PublicClient;\n\n /** Logger 实例 */\n private logger: Logger | null;\n\n /**\n * 构造函数\n *\n * @param client - viem PublicClient\n * @param options - 可选配置\n */\n constructor(client: PublicClient, options?: TokenDetectorOptions) {\n this.client = client;\n this.logger = options?.logger === null ? null : options?.logger || defaultLogger;\n }\n\n /**\n * 完整检测(同时获取支付能力和 Token 信息)\n * 优先从缓存读取,缓存未命中时执行检测并缓存结果\n *\n * @param tokenAddress - Token 地址\n * @returns 完整的检测结果\n */\n async detect(tokenAddress: string): Promise<TokenDetectionResult> {\n const cacheKey = await this.getCacheKey(tokenAddress);\n\n // 检查缓存\n const cached = this.cache.get(cacheKey);\n if (cached) {\n this.logger?.log(`💾 Using cached result for token ${tokenAddress}`);\n return cached;\n }\n\n // 并行执行检测\n this.logger?.log(`🔍 Detecting token ${tokenAddress}...`);\n const [capabilities, info] = await Promise.all([\n detectTokenPaymentMethods(tokenAddress, this.client, this.logger),\n getTokenInfo(tokenAddress, this.client, this.logger),\n ]);\n\n const result: TokenDetectionResult = {\n ...capabilities,\n ...info,\n };\n\n // 存入缓存\n this.cache.set(cacheKey, result);\n\n return result;\n }\n\n /**\n * 获取推荐的支付方式\n * 优先级:eip3009 > permit > permit2\n *\n * @param tokenAddress - Token 地址\n * @returns 推荐的支付方式\n */\n async getRecommendedMethod(\n tokenAddress: string,\n ): Promise<\"eip3009\" | \"permit\" | \"permit2\" | null> {\n const result = await this.detect(tokenAddress);\n const { supportedMethods } = result;\n\n if (supportedMethods.includes(\"eip3009\")) return \"eip3009\";\n if (supportedMethods.includes(\"permit\")) return \"permit\";\n if (supportedMethods.includes(\"permit2\") || supportedMethods.includes(\"permit2-witness\")) {\n return \"permit2\";\n }\n\n return null;\n }\n\n /**\n * 批量初始化(预热缓存)\n * 并行检测多个 Token 并缓存结果\n *\n * @param tokenAddresses - Token 地址列表\n * @returns 检测结果数组\n */\n async initialize(tokenAddresses: string[]): Promise<TokenDetectionResult[]> {\n this.logger?.log(`🔥 Warming up cache for ${tokenAddresses.length} tokens...`);\n\n // 并行检测所有 token\n const results = await Promise.all(\n tokenAddresses.map(address =>\n this.detect(address).catch(error => {\n this.logger?.error(`Failed to detect token ${address}:`, error);\n return null;\n }),\n ),\n );\n\n const successCount = results.filter(r => r !== null).length;\n this.logger?.log(`✅ Successfully detected ${successCount}/${tokenAddresses.length} tokens`);\n\n return results.filter((r): r is TokenDetectionResult => r !== null);\n }\n\n /**\n * 清除缓存\n *\n * @param tokenAddress - 可选,指定要清除的 Token 地址\n */\n async clearCache(tokenAddress?: string): Promise<void> {\n if (tokenAddress) {\n const cacheKey = await this.getCacheKey(tokenAddress);\n this.cache.delete(cacheKey);\n this.logger?.log(`🗑️ Cleared cache for token ${tokenAddress}`);\n } else {\n this.cache.clear();\n this.logger?.log(`🗑️ Cleared all cache`);\n }\n }\n\n /**\n * 获取缓存统计\n *\n * @returns 缓存统计信息\n */\n getCacheStats(): {\n size: number;\n keys: string[];\n } {\n return {\n size: this.cache.size,\n keys: Array.from(this.cache.keys()),\n };\n }\n\n /**\n * 生成缓存键\n *\n * @param tokenAddress - Token 地址\n * @returns 缓存键\n */\n private async getCacheKey(tokenAddress: string): Promise<string> {\n const chainId = await this.client.getChainId();\n return `${chainId}:${tokenAddress.toLowerCase()}`;\n }\n}\n"],"mappings":"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","names":["defaultLogger","hasEIP3009","hasPermit","hasPermit2Approval","defaultLogger","_a"]}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@wtflabs/x402-detector",
3
+ "version": "0.0.1-beta.2",
4
+ "main": "./dist/cjs/index.js",
5
+ "module": "./dist/esm/index.js",
6
+ "types": "./dist/cjs/index.d.ts",
7
+ "keywords": [
8
+ "x402",
9
+ "erc20",
10
+ "permit",
11
+ "eip3009",
12
+ "permit2",
13
+ "token-detection"
14
+ ],
15
+ "license": "Apache-2.0",
16
+ "author": "WTFLabs",
17
+ "repository": "https://github.com/coinbase/x402",
18
+ "description": "Token payment capability detection for x402 protocol - detects EIP-2612 Permit, EIP-3009, and Permit2 support",
19
+ "devDependencies": {
20
+ "@eslint/js": "^9.24.0",
21
+ "@types/node": "^22.13.4",
22
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
23
+ "@typescript-eslint/parser": "^8.29.1",
24
+ "eslint": "^9.24.0",
25
+ "eslint-plugin-import": "^2.31.0",
26
+ "eslint-plugin-jsdoc": "^50.6.9",
27
+ "eslint-plugin-prettier": "^5.2.6",
28
+ "prettier": "3.5.2",
29
+ "tsup": "^8.4.0",
30
+ "tsx": "^4.19.2",
31
+ "typescript": "^5.7.3",
32
+ "vite": "^6.2.6",
33
+ "vite-tsconfig-paths": "^5.1.4",
34
+ "vitest": "^3.0.5"
35
+ },
36
+ "dependencies": {
37
+ "viem": "^2.21.26"
38
+ },
39
+ "exports": {
40
+ ".": {
41
+ "import": {
42
+ "types": "./dist/esm/index.d.mts",
43
+ "default": "./dist/esm/index.mjs"
44
+ },
45
+ "require": {
46
+ "types": "./dist/cjs/index.d.ts",
47
+ "default": "./dist/cjs/index.js"
48
+ }
49
+ }
50
+ },
51
+ "files": [
52
+ "dist"
53
+ ],
54
+ "scripts": {
55
+ "start": "tsx --env-file=.env index.ts",
56
+ "test": "vitest run",
57
+ "test:watch": "vitest",
58
+ "build": "tsup",
59
+ "watch": "tsc --watch",
60
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
61
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
62
+ "lint": "eslint . --ext .ts --fix",
63
+ "lint:check": "eslint . --ext .ts"
64
+ }
65
+ }