84 lines
2.2 KiB
TypeScript
84 lines
2.2 KiB
TypeScript
export type ProjectSettingsIssue = {
|
||
code?: string;
|
||
field?: string;
|
||
message?: string;
|
||
};
|
||
|
||
export type ProjectSettingsErrorRecovery =
|
||
| 'refresh_and_freeze'
|
||
| 'reload_revision'
|
||
| 'retry_after_switch'
|
||
| 'show_message';
|
||
|
||
export class ProjectSettingsApiError extends Error {
|
||
readonly status: number;
|
||
readonly code: string;
|
||
readonly issues: ProjectSettingsIssue[];
|
||
|
||
constructor(input: {
|
||
status: number;
|
||
code: string;
|
||
message: string;
|
||
issues?: ProjectSettingsIssue[];
|
||
}) {
|
||
super(input.message);
|
||
this.name = 'ProjectSettingsApiError';
|
||
this.status = input.status;
|
||
this.code = input.code;
|
||
this.issues = input.issues ?? [];
|
||
}
|
||
}
|
||
|
||
export function parseProjectSettingsErrorBody(text: string): {
|
||
message: string;
|
||
code: string;
|
||
issues: ProjectSettingsIssue[];
|
||
} {
|
||
try {
|
||
const parsed = JSON.parse(text) as {
|
||
error?: string;
|
||
message?: string;
|
||
issues?: ProjectSettingsIssue[];
|
||
};
|
||
const issues = Array.isArray(parsed.issues) ? parsed.issues : [];
|
||
const issueText = issues
|
||
.map((issue) => issue.message)
|
||
.filter((msg): msg is string => Boolean(msg))
|
||
.join(';');
|
||
return {
|
||
code: String(parsed.error || ''),
|
||
message: issueText || parsed.message || parsed.error || text || '请求失败',
|
||
issues,
|
||
};
|
||
} catch {
|
||
return { code: '', message: text || '请求失败', issues: [] };
|
||
}
|
||
}
|
||
|
||
export function projectSettingsErrorFromResponse(
|
||
status: number,
|
||
text: string,
|
||
fallback: string,
|
||
): ProjectSettingsApiError {
|
||
const parsed = parseProjectSettingsErrorBody(text);
|
||
return new ProjectSettingsApiError({
|
||
status,
|
||
code: parsed.code,
|
||
message: parsed.message || fallback,
|
||
issues: parsed.issues,
|
||
});
|
||
}
|
||
|
||
export function recoverFromProjectSettingsError(
|
||
err: Pick<ProjectSettingsApiError, 'code' | 'status'>,
|
||
): ProjectSettingsErrorRecovery {
|
||
if (err.code === 'PLAN_FROZEN') return 'refresh_and_freeze';
|
||
if (err.code === 'PLAN_REVISION_CONFLICT') return 'reload_revision';
|
||
if (err.code === 'ERR_CONCURRENT_SWITCH') return 'retry_after_switch';
|
||
return 'show_message';
|
||
}
|
||
|
||
export function retainDraftOnFrozenConflict<T>(draft: T): T {
|
||
return draft;
|
||
}
|