828 lines
22 KiB
Markdown
828 lines
22 KiB
Markdown
---
|
||
name: walrus-debugger
|
||
description: Walrus 错误诊断专家 - 智能分析和解决 Walrus 应用中的问题
|
||
parameters:
|
||
- name: error-type
|
||
type: string
|
||
description: 错误类型 (upload/download/network/configuration/timeout/authentication)
|
||
required: true
|
||
- name: error-context
|
||
type: string
|
||
description: 错误信息、代码片段或问题描述
|
||
required: true
|
||
- name: environment
|
||
type: string
|
||
description: 运行环境 (browser/nodejs/nextjs/production)
|
||
default: nodejs
|
||
---
|
||
|
||
# Walrus 错误诊断技能
|
||
|
||
## 技能概述
|
||
|
||
`walrus-debugger` 是专业的错误诊断助手,专门分析 Walrus 应用中的各种问题,提供智能的解决方案和预防措施。
|
||
|
||
## 常见错误类型及解决方案
|
||
|
||
### 1. 上传失败错误
|
||
|
||
#### 错误类型: Upload Failed
|
||
```typescript
|
||
// 错误示例
|
||
Error: Upload failed: insufficient storage balance
|
||
Error: Upload failed: blob size exceeds maximum limit
|
||
Error: Upload failed: transaction rejected
|
||
|
||
// 诊断和解决方案
|
||
class UploadErrorDiagnostics {
|
||
async diagnoseUploadError(error: Error, context: UploadContext): Promise<Diagnosis> {
|
||
const analysis = this.analyzeErrorMessage(error.message);
|
||
|
||
switch (analysis.type) {
|
||
case 'INSUFFICIENT_BALANCE':
|
||
return this.diagnoseBalanceError(context);
|
||
case 'SIZE_LIMIT':
|
||
return this.diagnoseSizeError(context);
|
||
case 'TRANSACTION_REJECTED':
|
||
return this.diagnoseTransactionError(error, context);
|
||
case 'TIMEOUT':
|
||
return this.diagnoseTimeoutError(context);
|
||
default:
|
||
return this.diagnoseGenericUploadError(error, context);
|
||
}
|
||
}
|
||
|
||
private async diagnoseBalanceError(context: UploadContext): Promise<Diagnosis> {
|
||
const walletBalance = await this.checkWalletBalance(context.signerAddress);
|
||
|
||
return {
|
||
errorType: 'INSUFFICIENT_BALANCE',
|
||
description: '钱包余额不足,无法支付存储费用',
|
||
causes: [
|
||
'WAL 代币余额不足',
|
||
'SUI 代币余额不足以支付 gas 费',
|
||
'存储成本计算错误'
|
||
],
|
||
solutions: [
|
||
{
|
||
title: '检查并充值钱包',
|
||
code: `
|
||
// 检查钱包余额
|
||
async function checkBalances(address: string) {
|
||
const suiClient = new SuiClient({ url: getFullnodeUrl('testnet') });
|
||
const balance = await suiClient.getBalance({ owner: address });
|
||
|
||
console.log('SUI Balance:', balance.totalBalance);
|
||
|
||
// 检查 WAL 余额(如果支持)
|
||
const walBalance = await suiClient.getBalance({
|
||
owner: address,
|
||
coinType: '0x2::sui::SUI' // 替换为 WAL coin type
|
||
});
|
||
|
||
return { sui: balance.totalBalance, wal: walBalance.totalBalance };
|
||
}`
|
||
},
|
||
{
|
||
title: '优化存储策略',
|
||
code: `
|
||
// 使用更短的存储周期
|
||
const result = await client.walrus.writeFiles({
|
||
files: [file],
|
||
epochs: 5, // 减少存储周期
|
||
deletable: true,
|
||
signer: keypair
|
||
});
|
||
|
||
// 或者使用更小的文件
|
||
function compressFile(file: File): Promise<File> {
|
||
return new Promise((resolve) => {
|
||
if (file.size > 10 * 1024 * 1024) { // 10MB
|
||
// 压缩或分割文件
|
||
compressImage(file).then(resolve);
|
||
} else {
|
||
resolve(file);
|
||
}
|
||
});
|
||
}`
|
||
}
|
||
],
|
||
prevention: [
|
||
'定期检查钱包余额',
|
||
'设置余额预警',
|
||
'实施费用估算功能'
|
||
]
|
||
};
|
||
}
|
||
|
||
private async diagnoseSizeError(context: UploadContext): Promise<Diagnosis> {
|
||
const fileStats = await this.analyzeFileSize(context.file);
|
||
|
||
return {
|
||
errorType: 'SIZE_LIMIT',
|
||
description: '文件大小超出限制',
|
||
causes: [
|
||
`文件大小 ${fileStats.size}MB 超出限制`,
|
||
'网络环境不支持大文件传输',
|
||
'客户端内存不足'
|
||
],
|
||
solutions: [
|
||
{
|
||
title: '文件压缩',
|
||
code: `
|
||
// 图片压缩
|
||
async function compressImage(file: File, quality = 0.8): Promise<File> {
|
||
return new Promise((resolve) => {
|
||
const canvas = document.createElement('canvas');
|
||
const ctx = canvas.getContext('2d')!;
|
||
const img = new Image();
|
||
|
||
img.onload = () => {
|
||
// 计算压缩后的尺寸
|
||
const scale = Math.min(1, 1920 / Math.max(img.width, img.height));
|
||
canvas.width = img.width * scale;
|
||
canvas.height = img.height * scale;
|
||
|
||
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||
|
||
canvas.toBlob((blob) => {
|
||
resolve(new File([blob!], file.name, { type: file.type }));
|
||
}, file.type, quality);
|
||
};
|
||
|
||
img.src = URL.createObjectURL(file);
|
||
});
|
||
}
|
||
|
||
// 文本文件压缩
|
||
async function compressTextFile(file: File): Promise<File> {
|
||
const text = await file.text();
|
||
const compressed = await this.compressString(text);
|
||
return new File([compressed], file.name + '.gz', { type: 'application/gzip' });
|
||
}`
|
||
},
|
||
{
|
||
title: '文件分割上传',
|
||
code: `
|
||
// 分割大文件
|
||
async function splitAndUpload(file: File, chunkSize = 5 * 1024 * 1024): Promise<string[]> {
|
||
const chunks: string[] = [];
|
||
const totalChunks = Math.ceil(file.size / chunkSize);
|
||
|
||
for (let i = 0; i < totalChunks; i++) {
|
||
const start = i * chunkSize;
|
||
const end = Math.min(start + chunkSize, file.size);
|
||
const chunk = file.slice(start, end);
|
||
|
||
const chunkFile = new File([chunk], \`\${file.name}.part\${i}\`);
|
||
const result = await uploadSingleFile(chunkFile);
|
||
chunks.push(result.blobId);
|
||
}
|
||
|
||
return chunks;
|
||
}
|
||
|
||
// 创建清单文件记录分割信息
|
||
async function createManifest(chunks: string[], originalFile: File): Promise<string> {
|
||
const manifest = {
|
||
originalName: originalFile.name,
|
||
originalSize: originalFile.size,
|
||
chunks: chunks.map((blobId, index) => ({
|
||
blobId,
|
||
index,
|
||
size: originalFile.size / chunks.length
|
||
}))
|
||
};
|
||
|
||
const manifestBlob = new Blob([JSON.stringify(manifest)], { type: 'application/json' });
|
||
const manifestFile = new File([manifestBlob], \`\${originalFile.name}.manifest\`);
|
||
|
||
const result = await uploadSingleFile(manifestFile);
|
||
return result.blobId;
|
||
}`
|
||
}
|
||
],
|
||
prevention: [
|
||
'预先检查文件大小',
|
||
'实施文件大小限制',
|
||
'提供文件压缩工具'
|
||
]
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 网络连接错误
|
||
|
||
#### 错误类型: Network Error
|
||
```typescript
|
||
// 错误示例
|
||
Error: Network timeout
|
||
Error: Connection refused
|
||
Error: Failed to fetch
|
||
|
||
class NetworkErrorDiagnostics {
|
||
async diagnoseNetworkError(error: Error, context: NetworkContext): Promise<Diagnosis> {
|
||
const networkStatus = await this.checkNetworkStatus();
|
||
|
||
return {
|
||
errorType: 'NETWORK_ERROR',
|
||
description: '网络连接问题',
|
||
causes: [
|
||
'网络连接不稳定',
|
||
'RPC 节点不可用',
|
||
'防火墙或代理阻止连接',
|
||
'DNS 解析失败'
|
||
],
|
||
solutions: [
|
||
{
|
||
title: '实施自动重试机制',
|
||
code: `
|
||
class RobustWalrusClient {
|
||
private readonly MAX_RETRIES = 3;
|
||
private readonly RETRY_DELAY_BASE = 1000;
|
||
|
||
async withRetry<T>(
|
||
operation: () => Promise<T>,
|
||
operationName: string
|
||
): Promise<T> {
|
||
for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) {
|
||
try {
|
||
return await operation();
|
||
} catch (error) {
|
||
if (!this.isRetryableError(error) || attempt === this.MAX_RETRIES) {
|
||
throw new Error(\`\${operationName} failed after \${attempt} attempts: \${error.message}\`);
|
||
}
|
||
|
||
const delay = this.RETRY_DELAY_BASE * Math.pow(2, attempt - 1);
|
||
console.warn(\`\${operationName} failed (attempt \${attempt}/\${this.MAX_RETRIES}), retrying in \${delay}ms\`);
|
||
|
||
await this.sleep(delay);
|
||
}
|
||
}
|
||
|
||
throw new Error('Should not reach here');
|
||
}
|
||
|
||
private isRetryableError(error: any): boolean {
|
||
return error instanceof RetryableWalrusClientError ||
|
||
error.message.includes('timeout') ||
|
||
error.message.includes('network') ||
|
||
error.message.includes('connection') ||
|
||
error.status >= 500; // Server errors
|
||
}
|
||
|
||
private sleep(ms: number): Promise<void> {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
|
||
async uploadFiles(files: WalrusFile[]): Promise<any> {
|
||
return this.withRetry(
|
||
() => this.client.walrus.writeFiles({ files, epochs: 10, signer: this.signer }),
|
||
'File upload'
|
||
);
|
||
}
|
||
}`
|
||
},
|
||
{
|
||
title: '实施节点健康检查',
|
||
code: `
|
||
// 节点健康检查
|
||
class NodeHealthChecker {
|
||
private nodes: string[] = [
|
||
'https://fullnode.testnet.sui.io',
|
||
'https://fullnode.testnet.sui.io:443',
|
||
// 添加备用节点
|
||
];
|
||
|
||
async getHealthyNode(): Promise<string> {
|
||
for (const node of this.nodes) {
|
||
try {
|
||
const response = await fetch(\`\${node}/\`, {
|
||
method: 'HEAD',
|
||
timeout: 5000
|
||
});
|
||
|
||
if (response.ok) {
|
||
console.log(\`Node \${node} is healthy\`);
|
||
return node;
|
||
}
|
||
} catch (error) {
|
||
console.warn(\`Node \${node} is unhealthy: \${error.message}\`);
|
||
}
|
||
}
|
||
|
||
throw new Error('No healthy nodes available');
|
||
}
|
||
|
||
async testAllNodes(): Promise<NodeHealth[]> {
|
||
const results = await Promise.allSettled(
|
||
this.nodes.map(async (node) => {
|
||
const startTime = Date.now();
|
||
try {
|
||
const response = await fetch(\`\${node}/\`, { method: 'HEAD', timeout: 5000 });
|
||
return {
|
||
node,
|
||
healthy: response.ok,
|
||
responseTime: Date.now() - startTime,
|
||
error: null
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
node,
|
||
healthy: false,
|
||
responseTime: Date.now() - startTime,
|
||
error: error.message
|
||
};
|
||
}
|
||
})
|
||
);
|
||
|
||
return results.map(result =>
|
||
result.status === 'fulfilled' ? result.value : null
|
||
).filter(Boolean) as NodeHealth[];
|
||
}
|
||
}`
|
||
},
|
||
{
|
||
title: '配置超时和重试',
|
||
code: `
|
||
// 配置自定义超时和重试
|
||
const client = new SuiJsonRpcClient({
|
||
url: getFullnodeUrl('testnet'),
|
||
network: 'testnet',
|
||
}).$extend(walrus({
|
||
storageNodeClientOptions: {
|
||
timeout: 60000, // 60秒超时
|
||
retryAttempts: 5,
|
||
retryDelay: 2000,
|
||
onError: (error) => {
|
||
console.error('Storage node error:', error);
|
||
// 发送错误到监控系统
|
||
this.trackError('storage_node_error', error);
|
||
}
|
||
}
|
||
}));`
|
||
}
|
||
],
|
||
prevention: [
|
||
'实施网络监控',
|
||
'使用多个备用节点',
|
||
'设置合理的超时时间',
|
||
'实施断路器模式'
|
||
]
|
||
};
|
||
}
|
||
|
||
private async checkNetworkStatus(): Promise<NetworkStatus> {
|
||
try {
|
||
const response = await fetch('https://httpbin.org/ip');
|
||
const ipInfo = await response.json();
|
||
|
||
return {
|
||
online: true,
|
||
ip: ipInfo.ip,
|
||
latency: Date.now() - this.startTime
|
||
};
|
||
} catch (error) {
|
||
return {
|
||
online: false,
|
||
error: error.message
|
||
};
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 配置错误
|
||
|
||
#### 错误类型: Configuration Error
|
||
```typescript
|
||
// 错误示例
|
||
Error: Invalid network configuration
|
||
Error: WASM module failed to load
|
||
Error: Invalid signer configuration
|
||
|
||
class ConfigurationErrorDiagnostics {
|
||
async diagnoseConfigError(error: Error, context: ConfigContext): Promise<Diagnosis> {
|
||
return {
|
||
errorType: 'CONFIGURATION_ERROR',
|
||
description: '配置问题',
|
||
causes: [
|
||
'网络配置错误',
|
||
'WASM 模块路径错误',
|
||
'签名者配置无效',
|
||
'环境变量缺失'
|
||
],
|
||
solutions: [
|
||
{
|
||
title: '验证配置完整性',
|
||
code: `
|
||
// 配置验证器
|
||
class ConfigValidator {
|
||
static validateWalrusConfig(config: any): ValidationResult {
|
||
const errors: string[] = [];
|
||
|
||
// 验证必需字段
|
||
if (!config.network) {
|
||
errors.push('network is required (testnet/mainnet)');
|
||
}
|
||
|
||
if (!config.rpcUrl) {
|
||
errors.push('rpcUrl is required');
|
||
}
|
||
|
||
// 验证网络配置
|
||
if (config.network && !['testnet', 'mainnet'].includes(config.network)) {
|
||
errors.push('network must be either "testnet" or "mainnet"');
|
||
}
|
||
|
||
// 验证 URL 格式
|
||
if (config.rpcUrl && !this.isValidUrl(config.rpcUrl)) {
|
||
errors.push('rpcUrl must be a valid URL');
|
||
}
|
||
|
||
// 验证超时配置
|
||
if (config.timeout && (config.timeout < 1000 || config.timeout > 300000)) {
|
||
errors.push('timeout must be between 1000ms and 300000ms');
|
||
}
|
||
|
||
return {
|
||
valid: errors.length === 0,
|
||
errors
|
||
};
|
||
}
|
||
|
||
static async validateEnvironment(): Promise<ValidationResult> {
|
||
const errors: string[] = [];
|
||
|
||
// 检查环境变量
|
||
const requiredEnvVars = ['WALRUS_NETWORK', 'SUI_RPC_URL'];
|
||
for (const envVar of requiredEnvVars) {
|
||
if (!process.env[envVar]) {
|
||
errors.push(\`Environment variable \${envVar} is required\`);
|
||
}
|
||
}
|
||
|
||
// 检查 WASM 支持
|
||
if (typeof window !== 'undefined' && !window.WebAssembly) {
|
||
errors.push('WebAssembly is not supported in this browser');
|
||
}
|
||
|
||
// 验证网络连接
|
||
try {
|
||
const response = await fetch(process.env.SUI_RPC_URL!, {
|
||
method: 'HEAD',
|
||
timeout: 5000
|
||
});
|
||
if (!response.ok) {
|
||
errors.push('Cannot connect to SUI RPC endpoint');
|
||
}
|
||
} catch (error) {
|
||
errors.push(\`Network connection failed: \${error.message}\`);
|
||
}
|
||
|
||
return {
|
||
valid: errors.length === 0,
|
||
errors
|
||
};
|
||
}
|
||
|
||
private static isValidUrl(string: string): boolean {
|
||
try {
|
||
new URL(string);
|
||
return true;
|
||
} catch {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 使用配置验证
|
||
const config = {
|
||
network: 'testnet',
|
||
rpcUrl: getFullnodeUrl('testnet'),
|
||
timeout: 30000
|
||
};
|
||
|
||
const validation = ConfigValidator.validateWalrusConfig(config);
|
||
if (!validation.valid) {
|
||
console.error('Configuration validation failed:', validation.errors);
|
||
throw new Error('Invalid configuration');
|
||
}`
|
||
},
|
||
{
|
||
title: 'WASM 模块配置',
|
||
code: `
|
||
// WASM 配置验证和加载
|
||
class WasmLoader {
|
||
static async loadWasmWithFallback(): Promise<any> {
|
||
const strategies = [
|
||
() => this.loadFromBundle(),
|
||
() => this.loadFromCDN(),
|
||
() => this.loadFromLocal()
|
||
];
|
||
|
||
for (const strategy of strategies) {
|
||
try {
|
||
const wasm = await strategy();
|
||
console.log('WASM loaded successfully');
|
||
return wasm;
|
||
} catch (error) {
|
||
console.warn('WASM loading strategy failed:', error.message);
|
||
}
|
||
}
|
||
|
||
throw new Error('All WASM loading strategies failed');
|
||
}
|
||
|
||
private static async loadFromBundle(): Promise<any> {
|
||
// Vite 示例
|
||
if (typeof import.meta !== 'undefined' && import.meta.url) {
|
||
const wasmUrl = new URL('@mysten/walrus-wasm/web/walrus_wasm_bg.wasm', import.meta.url);
|
||
const wasmModule = await import(wasmUrl);
|
||
return wasmModule.default;
|
||
}
|
||
throw new Error('Bundle loading not supported');
|
||
}
|
||
|
||
private static async loadFromCDN(): Promise<any> {
|
||
const cdnUrls = [
|
||
'https://unpkg.com/@mysten/walrus-wasm@latest/web/walrus_wasm_bg.wasm',
|
||
'https://cdn.jsdelivr.net/npm/@mysten/walrus-wasm@latest/web/walrus_wasm_bg.wasm'
|
||
];
|
||
|
||
for (const url of cdnUrls) {
|
||
try {
|
||
const response = await fetch(url);
|
||
if (response.ok) {
|
||
const wasmBuffer = await response.arrayBuffer();
|
||
return WebAssembly.compile(wasmBuffer);
|
||
}
|
||
} catch (error) {
|
||
console.warn(\`CDN loading failed for \${url}:\`, error.message);
|
||
}
|
||
}
|
||
|
||
throw new Error('All CDN URLs failed');
|
||
}
|
||
|
||
private static async loadFromLocal(): Promise<any> {
|
||
// 尝试从本地路径加载
|
||
const localPaths = [
|
||
'/walrus/walrus_wasm_bg.wasm',
|
||
'./node_modules/@mysten/walrus-wasm/web/walrus_wasm_bg.wasm'
|
||
];
|
||
|
||
for (const path of localPaths) {
|
||
try {
|
||
const response = await fetch(path);
|
||
if (response.ok) {
|
||
const wasmBuffer = await response.arrayBuffer();
|
||
return WebAssembly.compile(wasmBuffer);
|
||
}
|
||
} catch (error) {
|
||
console.warn(\`Local loading failed for \${path}:\`, error.message);
|
||
}
|
||
}
|
||
|
||
throw new Error('Local loading failed');
|
||
}
|
||
}
|
||
|
||
// 在客户端配置中使用 WASM
|
||
const client = new SuiJsonRpcClient({
|
||
url: getFullnodeUrl('testnet'),
|
||
network: 'testnet',
|
||
}).$extend(
|
||
walrus({
|
||
wasmUrl: await WasmLoader.loadWasmWithFallback()
|
||
})
|
||
);`
|
||
}
|
||
],
|
||
prevention: [
|
||
'实施配置验证',
|
||
'提供配置模板',
|
||
'设置默认值',
|
||
'实施配置监控'
|
||
]
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 浏览器环境特定错误
|
||
|
||
#### 错误类型: Browser Environment Error
|
||
```typescript
|
||
class BrowserErrorDiagnostics {
|
||
async diagnoseBrowserError(error: Error, context: BrowserContext): Promise<Diagnosis> {
|
||
return {
|
||
errorType: 'BROWSER_ERROR',
|
||
description: '浏览器环境特定问题',
|
||
causes: [
|
||
'WASM 不支持',
|
||
'CORS 策略阻止',
|
||
'内存不足',
|
||
'钱包连接问题'
|
||
],
|
||
solutions: [
|
||
{
|
||
title: '浏览器兼容性检查',
|
||
code: `
|
||
// 浏览器兼容性检查
|
||
class BrowserCompatibility {
|
||
static checkCompatibility(): BrowserCompatibilityResult {
|
||
const checks = {
|
||
webAssembly: this.checkWebAssembly(),
|
||
fetch: this.checkFetch(),
|
||
blob: this.checkBlob(),
|
||
fileAPI: this.checkFileAPI(),
|
||
webWorkers: this.checkWebWorkers(),
|
||
localStorage: this.checkLocalStorage()
|
||
};
|
||
|
||
const supported = Object.values(checks).every(check => check.supported);
|
||
|
||
return {
|
||
supported,
|
||
checks,
|
||
recommendations: this.getRecommendations(checks)
|
||
};
|
||
}
|
||
|
||
private static checkWebAssembly(): CompatibilityCheck {
|
||
const supported = typeof WebAssembly === 'object' && WebAssembly !== null;
|
||
return {
|
||
supported,
|
||
message: supported ? 'WebAssembly is supported' : 'WebAssembly is not supported',
|
||
fix: !supported ? 'Please use a modern browser that supports WebAssembly' : null
|
||
};
|
||
}
|
||
|
||
private static getRecommendations(checks: Record<string, CompatibilityCheck>): string[] {
|
||
const recommendations: string[] = [];
|
||
|
||
if (!checks.webAssembly.supported) {
|
||
recommendations.push('Upgrade to a modern browser (Chrome 57+, Firefox 52+, Safari 11+)');
|
||
}
|
||
|
||
if (!checks.webWorkers.supported) {
|
||
recommendations.push('Consider enabling Web Workers for better performance');
|
||
}
|
||
|
||
return recommendations;
|
||
}
|
||
}
|
||
|
||
// 使用兼容性检查
|
||
const compatibility = BrowserCompatibility.checkCompatibility();
|
||
if (!compatibility.supported) {
|
||
console.error('Browser compatibility issues:', compatibility.recommendations);
|
||
// 显示用户友好的错误信息
|
||
this.showCompatibilityError(compatibility);
|
||
}`
|
||
},
|
||
{
|
||
title: '内存管理',
|
||
code: `
|
||
// 浏览器内存管理
|
||
class BrowserMemoryManager {
|
||
private memoryThreshold = 100 * 1024 * 1024; // 100MB
|
||
|
||
async checkMemoryUsage(): Promise<MemoryUsage> {
|
||
if ('memory' in performance) {
|
||
const memory = (performance as any).memory;
|
||
return {
|
||
used: memory.usedJSHeapSize,
|
||
total: memory.totalJSHeapSize,
|
||
limit: memory.jsHeapSizeLimit,
|
||
usageRatio: memory.usedJSHeapSize / memory.jsHeapSizeLimit
|
||
};
|
||
}
|
||
return { used: 0, total: 0, limit: 0, usageRatio: 0 };
|
||
}
|
||
|
||
async manageMemoryForLargeFile(file: File): Promise<boolean> {
|
||
const memoryUsage = await this.checkMemoryUsage();
|
||
|
||
// 检查是否有足够内存处理文件
|
||
const estimatedUsage = file.size * 3; // 估算处理过程中需要的内存
|
||
const availableMemory = memoryUsage.limit - memoryUsage.used;
|
||
|
||
if (estimatedUsage > availableMemory) {
|
||
console.warn('Insufficient memory for file processing');
|
||
|
||
// 触发垃圾回收(如果可能)
|
||
if (window.gc) {
|
||
window.gc();
|
||
}
|
||
|
||
// 重新检查内存
|
||
const newUsage = await this.checkMemoryUsage();
|
||
return (file.size * 3) < (newUsage.limit - newUsage.used);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 流式处理大文件
|
||
async processLargeFileInChunks(
|
||
file: File,
|
||
processor: (chunk: Uint8Array) => Promise<void>,
|
||
chunkSize = 1024 * 1024 // 1MB
|
||
): Promise<void> {
|
||
const reader = new FileReader();
|
||
let offset = 0;
|
||
|
||
const processChunk = () => {
|
||
if (offset >= file.size) {
|
||
return Promise.resolve();
|
||
}
|
||
|
||
return new Promise<void>((resolve, reject) => {
|
||
const chunk = file.slice(offset, offset + chunkSize);
|
||
|
||
reader.onload = async (event) => {
|
||
try {
|
||
if (event.target?.result) {
|
||
const arrayBuffer = event.target.result as ArrayBuffer;
|
||
const uint8Array = new Uint8Array(arrayBuffer);
|
||
|
||
await processor(uint8Array);
|
||
offset += chunkSize;
|
||
|
||
// 检查内存使用
|
||
const memoryUsage = await this.checkMemoryUsage();
|
||
if (memoryUsage.usageRatio > 0.8) {
|
||
console.warn('High memory usage, forcing garbage collection');
|
||
if (window.gc) window.gc();
|
||
}
|
||
|
||
await processChunk();
|
||
}
|
||
resolve();
|
||
} catch (error) {
|
||
reject(error);
|
||
}
|
||
};
|
||
|
||
reader.onerror = () => reject(new Error('File reading failed'));
|
||
reader.readAsArrayBuffer(chunk);
|
||
});
|
||
};
|
||
|
||
return processChunk();
|
||
}
|
||
}`
|
||
}
|
||
],
|
||
prevention: [
|
||
'实施浏览器兼容性检查',
|
||
'监控内存使用',
|
||
'提供降级方案',
|
||
'实施优雅错误处理'
|
||
]
|
||
};
|
||
}
|
||
}
|
||
```
|
||
|
||
## 使用方法
|
||
|
||
### 上传错误诊断
|
||
```typescript
|
||
skill: "walrus-debugger"
|
||
// error-type: upload
|
||
// error-context: Upload failed: insufficient storage balance, trying to upload 10MB file to testnet
|
||
// environment: browser
|
||
```
|
||
|
||
### 网络连接问题
|
||
```typescript
|
||
skill: "walrus-debugger"
|
||
// error-type: network
|
||
// error-context: Network timeout when connecting to storage nodes, intermittent connection issues
|
||
// environment: production
|
||
```
|
||
|
||
### 配置验证
|
||
```typescript
|
||
skill: "walrus-debugger"
|
||
// error-type: configuration
|
||
// error-context: WASM module failed to load in Vite production build, error: "Failed to resolve module"
|
||
// environment: nodejs
|
||
```
|
||
|
||
### 性能问题
|
||
```typescript
|
||
skill: "walrus-debugger"
|
||
// error-type: performance
|
||
// error-context: Memory usage spikes when uploading multiple large files, browser crashes
|
||
// environment: browser
|
||
```
|
||
|
||
---
|
||
|
||
*更新时间:2025-11-11* |