docs(claude): restructure documentation management and add feedback system

- Restructure §8 from "文档沉淀规则" to "文档管理规则" with 4 subsections
  - Add docs/ structure with features/ and knowledge-base/ directories
  - Add feature documentation template with 7 sections (概述/设计初衷/技术设计/预期作用/实际效果/演化路线/头脑风暴)
  - Add feature update trigger matrix (新增/修改/完成/问题/反馈)
  - Add documentation quality checklist
- Add §16
This commit is contained in:
iven
2026-03-16 13:54:03 +08:00
parent 8e630882c7
commit adfd7024df
44 changed files with 10491 additions and 248 deletions

View File

@@ -0,0 +1,373 @@
/**
* ZCLAW Error Handling Utilities
*
* Centralized error reporting, notification, and tracking system.
*/
import { v4 as uuidv4 } from 'uuid';
import {
AppError,
classifyError,
ErrorCategory,
ErrorSeverity,
} from './error-types';
// === Error Store ===
interface StoredError extends AppError {
dismissed: boolean;
reported: boolean;
}
interface ErrorStore {
errors: StoredError[];
addError: (error: AppError) => void;
dismissError: (id: string) => void;
dismissAll: () => void;
markReported: (id: string) => void;
getUndismissedErrors: () => StoredError[];
getErrorCount: () => number;
getErrorsByCategory: (category: ErrorCategory) => StoredError[];
getErrorsBySeverity: (severity: ErrorSeverity) => StoredError[];
}
// === Global Error Store ===
let errorStore: ErrorStore = {
errors: [],
addError: () => {},
dismissError: () => {},
dismissAll: () => {},
markReported: () => {},
getUndismissedErrors: () => [],
getErrorCount: () => 0,
getErrorsByCategory: () => [],
getErrorsBySeverity: () => [],
};
// === Initialize Store ===
function initErrorStore(): void {
errorStore = {
errors: [],
addError: (error: AppError) => {
errorStore.errors = [error, ...errorStore.errors];
// Notify listeners
notifyErrorListeners(error);
},
dismissError: (id: string) => void {
const error = errorStore.errors.find(e => e.id === id);
if (error) {
errorStore.errors = errorStore.errors.map(e =>
e.id === id ? { ...e, dismissed: true } : e
);
}
},
dismissAll: () => void {
errorStore.errors = errorStore.errors.map(e => ({ ...e, dismissed: true }));
},
markReported: (id: string) => void {
const error = errorStore.errors.find(e => e.id === id);
if (error) {
errorStore.errors = errorStore.errors.map(e =>
e.id === id ? { ...e, reported: true } : e
);
}
},
getUndismissedErrors: () => StoredError[] => {
return errorStore.errors.filter(e => !e.dismissed);
},
getErrorCount: () => number => {
return errorStore.errors.filter(e => !e.dismissed).length;
},
getErrorsByCategory: (category: ErrorCategory) => StoredError[] => {
return errorStore.errors.filter(e => e.category === category && !e.dismissed);
},
getErrorsBySeverity: (severity: ErrorSeverity) => StoredError[] => {
return errorStore.errors.filter(e => e.severity === severity && !e.dismissed);
},
};
}
// === Error Listeners ===
type ErrorListener = (error: AppError) => void;
const errorListeners: Set<ErrorListener> = new Set();
function addErrorListener(listener: ErrorListener): () => void {
errorListeners.add(listener);
return () => errorListeners.delete(listener);
}
function notifyErrorListeners(error: AppError): void {
errorListeners.forEach(listener => {
try {
listener(error);
} catch (e) {
console.error('[ErrorHandling] Listener error:', e);
}
});
}
// Initialize on first import
initErrorStore();
// === Public API ===
/**
* Report an error to the centralized error handling system.
*/
export function reportError(
error: unknown,
context?: {
componentStack?: string;
errorName?: string;
errorMessage?: string;
}
): AppError {
const appError = classifyError(error);
// Add context information if provided
if (context) {
const technicalDetails = [
context.componentStack && `Component Stack:\n${context.componentStack}`,
context.errorName && `Error Name: ${context.errorName}`,
context.errorMessage && `Error Message: ${context.errorMessage}`,
].filter(Boolean).join('\n\n');
if (technicalDetails) {
(appError as { technicalDetails?: string }).technicalDetails = technicalDetails;
}
}
errorStore.addError(appError);
// Log to console in development
if (import.meta.env.DEV) {
console.error('[ErrorHandling] Error reported:', {
id: appError.id,
category: appError.category,
severity: appError.severity,
title: appError.title,
message: appError.message,
});
}
return appError;
}
/**
* Report an error from an API response.
*/
export function reportApiError(
response: Response,
endpoint: string,
method: string = 'GET'
): AppError {
const status = response.status;
let category: ErrorCategory = 'server';
let severity: ErrorSeverity = 'medium';
let title = 'API Error';
let message = `Request to ${endpoint} failed with status ${status}`;
let recoverySteps: { description: string }[] = [];
if (status === 401) {
category = 'auth';
severity = 'high';
title = 'Authentication Required';
message = 'Your session has expired. Please authenticate again.';
recoverySteps = [
{ description: 'Click "Reconnect" to authenticate' },
{ description: 'Check your API key in settings' },
];
} else if (status === 403) {
category = 'permission';
severity = 'medium';
title = 'Permission Denied';
message = 'You do not have permission to perform this action.';
recoverySteps = [
{ description: 'Contact your administrator for access' },
{ description: 'Check your RBAC configuration' },
];
} else if (status === 404) {
category = 'client';
severity = 'low';
title = 'Not Found';
message = `The requested resource was not found: ${endpoint}`;
recoverySteps = [
{ description: 'Verify the resource exists' },
{ description: 'Check the URL is correct' },
];
} else if (status === 422) {
category = 'validation';
severity = 'low';
title = 'Validation Error';
message = 'The request data is invalid.';
recoverySteps = [
{ description: 'Check your input data format' },
{ description: 'Verify required fields are provided' },
];
} else if (status === 429) {
category = 'client';
severity = 'medium';
title = 'Rate Limited';
message = 'Too many requests. Please wait before trying again.';
recoverySteps = [
{ description: 'Wait a moment before retrying' },
{ description: 'Reduce request frequency' },
];
} else if (status >= 500) {
category = 'server';
severity = 'high';
title = 'Server Error';
message = 'The server encountered an error processing your request.';
recoverySteps = [
{ description: 'Try again in a few moments' },
{ description: 'Contact support if the problem persists' },
];
}
const appError: AppError = {
id: uuidv4(),
category,
severity,
title,
message,
technicalDetails: `${method} ${endpoint}\nStatus: ${status}\nResponse: ${response.statusText}`,
recoverable: status !== 500 || status < 400,
recoverySteps,
timestamp: new Date(),
originalError: response,
};
errorStore.addError(appError);
return appError;
}
/**
* Report a network error.
*/
export function reportNetworkError(
error: Error,
url?: string
): AppError {
return reportError(error, {
errorMessage: url ? `URL: ${url}\n${error.message}` : error.message,
});
}
/**
* Report a WebSocket error.
*/
export function reportWebSocketError(
event: CloseEvent | ErrorEvent,
url: string
): AppError {
const code = 'code' in event ? event.code : 0;
const reason = 'reason' in event ? event.reason : 'Unknown';
return reportError(
new Error(`WebSocket error: ${reason} (code: ${code})`),
{
errorMessage: `WebSocket URL: ${url}\nCode: ${code}\nReason: ${reason}`,
}
);
}
/**
* Dismiss an error by ID.
*/
export function dismissError(id: string): void {
errorStore.dismissError(id);
}
/**
* Dismiss all active errors.
*/
export function dismissAllErrors(): void {
errorStore.dismissAll();
}
/**
* Mark an error as reported.
*/
export function markErrorReported(id: string): void {
errorStore.markReported(id);
}
/**
* Get all active (non-dismissed) errors.
*/
export function getActiveErrors(): StoredError[] {
return errorStore.getUndismissedErrors();
}
/**
* Get the count of active errors.
*/
export function getActiveErrorCount(): number {
return errorStore.getErrorCount();
}
/**
* Get errors filtered by category.
*/
export function getErrorsByCategory(category: ErrorCategory): StoredError[] {
return errorStore.getErrorsByCategory(category);
}
/**
* Get errors filtered by severity.
*/
export function getErrorsBySeverity(severity: ErrorSeverity): StoredError[] {
return errorStore.getErrorsBySeverity(severity);
}
/**
* Subscribe to error events.
*/
export function subscribeToErrors(listener: ErrorListener): () => void {
return addErrorListener(listener);
}
/**
* Check if there are any critical errors.
*/
export function hasCriticalErrors(): boolean {
return errorStore.getErrorsBySeverity('critical').length > 0;
}
/**
* Check if there are any high severity errors.
*/
export function hasHighSeverityErrors(): boolean {
const highSeverity = ['high', 'critical'];
return errorStore.errors.some(e => highSeverity.includes(e.severity) && !e.dismissed);
}
// === Types ===
interface CloseEvent {
code?: number;
reason?: string;
wasClean?: boolean;
}
interface ErrorEvent {
code?: number;
reason?: string;
message?: string;
}
export interface StoredError extends AppError {
dismissed: boolean;
reported: boolean;
}