262 lines
11 KiB
TypeScript
262 lines
11 KiB
TypeScript
export const WORKS_APP_ID_PATTERN = /^[a-z0-9][a-z0-9-]{2,79}$/;
|
||
|
||
export type WorksPublishFailure = {
|
||
title: string;
|
||
reason: string;
|
||
nextStep: string;
|
||
};
|
||
|
||
const LEGACY_BUILD_FAILURE: WorksPublishFailure = {
|
||
title: '旧版提交无法继续处理',
|
||
reason: '这是旧版客户端创建的云端构建任务,不能在新的本地构建链路中重试。',
|
||
nextStep: '升级 Makelore 并重新提交。',
|
||
};
|
||
|
||
const CLIENT_ARTIFACT_REJECTED: WorksPublishFailure = {
|
||
title: '本次构建结果未通过平台校验',
|
||
reason: '平台重新校验本次构建产物时发现摘要、文件或输出限制不匹配。',
|
||
nextStep: '升级 Makelore,并检查本次构建结果后重新提交。',
|
||
};
|
||
|
||
const PLATFORM_FINALIZATION_RETRYABLE: WorksPublishFailure = {
|
||
title: '平台校验任务暂未完成',
|
||
reason: '本次构建结果已上传,但平台校验或发布文件固化过程暂时中断。',
|
||
nextStep: '无需修改项目,请联系运营人员在“构建异常”中重新处理。',
|
||
};
|
||
|
||
const PUBLISH_FAILURES: Record<string, WorksPublishFailure> = {
|
||
LOCAL_BUILD_RUNTIME_UNAVAILABLE: {
|
||
title: '本地构建环境暂不可用',
|
||
reason: 'Makelore 安装包中的固定 Node/npm 构建组件无法启动。',
|
||
nextStep: '重新启动或升级 Makelore 后再提交。',
|
||
},
|
||
LOCAL_BUILD_FAILED: {
|
||
title: '本次构建结果未生成',
|
||
reason: '项目依赖安装或 Vite 构建没有成功完成。',
|
||
nextStep: '请让开发助手修复依赖或构建错误后重新提交。',
|
||
},
|
||
LOCAL_BUILD_TIMEOUT: {
|
||
title: '本次构建结果生成超时',
|
||
reason: '依赖安装或 Vite 构建超过了 Makelore 的时间限制。',
|
||
nextStep: '请精简依赖或构建步骤后重新提交。',
|
||
},
|
||
LOCAL_BUILD_OUTPUT_MISSING: {
|
||
title: '本次构建结果不完整',
|
||
reason: 'Vite 没有生成可发布的 index.html,或输出不符合静态作品限制。',
|
||
nextStep: '请检查 Vite 输出、资源大小和文件类型后重新提交。',
|
||
},
|
||
CLIENT_BUILD_PROTOCOL_REQUIRED: {
|
||
title: '需要升级 Makelore',
|
||
reason: '平台只接受新版 Makelore 生成的本地构建结果。',
|
||
nextStep: '升级 Makelore 并重新提交。',
|
||
},
|
||
PUBLISH_PREFLIGHT_LOAD_FAILED: {
|
||
title: '作品主页无法打开',
|
||
reason: 'Makelore 使用内置浏览器检查时,主页面没有成功加载。',
|
||
nextStep: '请让开发助手检查构建输出中的 index.html 和资源路径。',
|
||
},
|
||
PUBLISH_PREFLIGHT_RUNTIME_ERROR: {
|
||
title: '作品打开时发生错误',
|
||
reason: 'Makelore 的内置浏览器检测到页面运行错误。',
|
||
nextStep: '请让开发助手用项目预览修复运行错误后重新提交。',
|
||
},
|
||
PUBLISH_PREFLIGHT_BLANK: {
|
||
title: '作品打开后没有内容',
|
||
reason: '桌面或移动视口下没有检测到可见页面内容。',
|
||
nextStep: '请让开发助手检查首屏渲染、页面尺寸和移动端布局。',
|
||
},
|
||
PUBLISH_PREFLIGHT_TIMEOUT: {
|
||
title: '作品打开检查超时',
|
||
reason: '内置浏览器没有在规定时间内完成页面检查。',
|
||
nextStep: '请让开发助手检查首屏资源大小和启动逻辑。',
|
||
},
|
||
PUBLISH_PREFLIGHT_UNAVAILABLE: {
|
||
title: '暂时无法完成本地浏览器检查',
|
||
reason: 'Makelore 的内置浏览器当前不可用。',
|
||
nextStep: '重新启动 Makelore 后再提交,无需安装额外浏览器。',
|
||
},
|
||
AUTH_REQUIRED: {
|
||
title: '需要重新登录',
|
||
reason: '当前登录状态已失效,无法提交作品。',
|
||
nextStep: '重新登录 Makelore 后再提交。',
|
||
},
|
||
PROJECT_NOT_FOUND: {
|
||
title: '找不到项目文件夹',
|
||
reason: 'Makelore 无法读取当前项目。',
|
||
nextStep: '重新打开项目,确认文件夹仍然存在后再提交。',
|
||
},
|
||
PROJECT_FILE_MISSING: {
|
||
title: '项目文件不完整',
|
||
reason: '项目模板中的发布文件被删除或损坏。',
|
||
nextStep: '请让开发助手修复当前项目模板后重试。',
|
||
},
|
||
PROJECT_TYPE_UNPUBLISHABLE: {
|
||
title: '这个项目没有配置发布方式',
|
||
reason: '自定义项目只创建工作空间,不会自动生成平台发布模板。',
|
||
nextStep: '如需一键提交,请新建小游戏或小程序项目。',
|
||
},
|
||
PROJECT_FILE_INVALID: {
|
||
title: '项目配置无法读取',
|
||
reason: 'package.json 或 package-lock.json 格式不正确。',
|
||
nextStep: '请让开发助手修复项目配置文件后重试。',
|
||
},
|
||
VITE_NOT_DECLARED: {
|
||
title: '项目还不是可发布的 Vite 工程',
|
||
reason: 'package.json 中没有找到 Vite 依赖。',
|
||
nextStep: '请让开发助手补齐 Vite 配置并确认本地可以启动。',
|
||
},
|
||
PACKAGE_MANAGER_UNSUPPORTED: {
|
||
title: '项目缺少 npm 锁文件',
|
||
reason: '当前自动提交只支持带 package-lock.json 的 npm 项目。',
|
||
nextStep: '请让开发助手使用 npm 生成并提交 package-lock.json。',
|
||
},
|
||
LOCKFILE_UNSUPPORTED: {
|
||
title: 'npm 锁文件版本过旧',
|
||
reason: '当前 package-lock.json 不能用于安全构建。',
|
||
nextStep: '请让开发助手使用新版 npm 重新生成 package-lock.json。',
|
||
},
|
||
INDEX_HTML_EMPTY: {
|
||
title: '项目入口是空的',
|
||
reason: '根目录的 index.html 没有可用内容。',
|
||
nextStep: '请让开发助手修复页面入口,并确认本地可以打开。',
|
||
},
|
||
PROJECT_TOO_LARGE: {
|
||
title: '项目文件太大',
|
||
reason: '源码总大小超过平台允许范围。',
|
||
nextStep: '删除不需要的大文件和生成文件后重新提交。',
|
||
},
|
||
ARCHIVE_TOO_LARGE: {
|
||
title: '项目压缩包太大',
|
||
reason: '安全打包后的文件仍超过平台限制。',
|
||
nextStep: '移除不需要的图片、音视频或其他大文件后重试。',
|
||
},
|
||
PROJECT_TOO_MANY_FILES: {
|
||
title: '项目文件太多',
|
||
reason: '源码文件数量超过平台允许范围。',
|
||
nextStep: '清理缓存、生成文件和不再使用的素材后重试。',
|
||
},
|
||
PROJECT_SYMLINK_UNSUPPORTED: {
|
||
title: '项目包含不安全的链接文件',
|
||
reason: '自动提交不会跟随符号链接读取其他位置的文件。',
|
||
nextStep: '将需要的文件复制到项目内,并移除符号链接后重试。',
|
||
},
|
||
PROJECT_FILE_UNSUPPORTED: {
|
||
title: '项目包含不支持的文件',
|
||
reason: '部分文件类型无法安全打包。',
|
||
nextStep: '请让开发助手移除特殊文件后重试。',
|
||
},
|
||
PROJECT_PATH_UNSUPPORTED: {
|
||
title: '项目包含不支持的文件名',
|
||
reason: '部分路径无法在云端安全解压。',
|
||
nextStep: '请让开发助手重命名异常文件或文件夹后重试。',
|
||
},
|
||
PROJECT_CHANGED_DURING_PACKAGING: {
|
||
title: '项目正在发生变化',
|
||
reason: '打包时有文件仍在写入,无法保证提交内容完整。',
|
||
nextStep: '等待开发助手或其他程序保存完成后重新提交。',
|
||
},
|
||
PROJECT_SUBMISSION_CONFLICT: {
|
||
title: '这个作品已有版本在处理中',
|
||
reason: '平台正在构建或审核上一版,暂时不能重复提交。',
|
||
nextStep: '等待当前版本完成审核或被驳回后再提交。',
|
||
},
|
||
PROJECT_CREATE_REJECTED: {
|
||
title: '作品信息没有通过检查',
|
||
reason: '平台没有接受自动生成的作品信息。',
|
||
nextStep: '请联系运营人员检查作品信息。',
|
||
},
|
||
PROJECT_OWNERSHIP_UNCONFIRMED: {
|
||
title: '作品标识已被占用',
|
||
reason: '自动生成的作品标识不属于当前账号。',
|
||
nextStep: '请联系运营人员处理作品归属。',
|
||
},
|
||
SOURCE_PACKAGE_REJECTED: {
|
||
title: '本次构建结果没有通过平台检查',
|
||
reason: '平台对上传字节和归档结构的独立校验没有通过。',
|
||
nextStep: '请检查源码与本次构建结果的文件结构后重新提交。',
|
||
},
|
||
PUBLISH_FORBIDDEN: {
|
||
title: '当前账号不能发布这个作品',
|
||
reason: '平台没有确认当前账号的作品权限。',
|
||
nextStep: '确认登录了正确账号;如果仍然失败,请联系运营人员。',
|
||
},
|
||
WORKS_SQUARE_UNAVAILABLE: {
|
||
title: '发布服务暂时不可用',
|
||
reason: '平台当前没有完成这次提交。',
|
||
nextStep: '请稍后重新提交;自动重试不会创建重复版本。',
|
||
},
|
||
BUILD_STATUS_UNAVAILABLE: {
|
||
title: '暂时无法读取构建进度',
|
||
reason: '作品已经提交,但 Makelore 暂时没有拿到最新状态。',
|
||
nextStep: '稍后重新打开项目查看,不需要重复提交。',
|
||
},
|
||
BUILD_STATUS_TIMEOUT: {
|
||
title: '构建时间比平时更长',
|
||
reason: '作品仍可能在云端处理中。',
|
||
nextStep: '稍后重新打开项目查看,不需要重复提交。',
|
||
},
|
||
DEPENDENCY_PREFETCH_FAILED: LEGACY_BUILD_FAILURE,
|
||
DEPENDENCY_PREFETCH_TIMEOUT: LEGACY_BUILD_FAILURE,
|
||
BUILD_COMMAND_FAILED: LEGACY_BUILD_FAILURE,
|
||
BUILD_TIMEOUT: LEGACY_BUILD_FAILURE,
|
||
BUILD_SANDBOX_UNAVAILABLE: LEGACY_BUILD_FAILURE,
|
||
BUILD_PIPELINE_MISMATCH: LEGACY_BUILD_FAILURE,
|
||
BUILD_PIPELINE_UNSUPPORTED: LEGACY_BUILD_FAILURE,
|
||
SOURCE_DIGEST_MISMATCH: CLIENT_ARTIFACT_REJECTED,
|
||
OUTPUT_MISSING: CLIENT_ARTIFACT_REJECTED,
|
||
OUTPUT_INVALID: CLIENT_ARTIFACT_REJECTED,
|
||
OUTPUT_LIMIT_EXCEEDED: CLIENT_ARTIFACT_REJECTED,
|
||
CLIENT_ARTIFACT_INVALID: CLIENT_ARTIFACT_REJECTED,
|
||
CLIENT_ARTIFACT_DIGEST_MISMATCH: CLIENT_ARTIFACT_REJECTED,
|
||
VERSION_NOT_FOUND: {
|
||
title: '平台没有找到本次提交',
|
||
reason: '构建服务无法读取刚刚上传的版本。',
|
||
nextStep: '请重新提交;如果持续失败,请联系运营人员。',
|
||
},
|
||
RELEASE_STORE_FAILED: {
|
||
...PLATFORM_FINALIZATION_RETRYABLE,
|
||
},
|
||
ARTIFACT_STORAGE_UNAVAILABLE: PLATFORM_FINALIZATION_RETRYABLE,
|
||
BUILD_STALE: PLATFORM_FINALIZATION_RETRYABLE,
|
||
BROWSER_SMOKE_FAILED: LEGACY_BUILD_FAILURE,
|
||
BROWSER_SMOKE_TIMEOUT: LEGACY_BUILD_FAILURE,
|
||
BROWSER_SMOKE_UNAVAILABLE: LEGACY_BUILD_FAILURE,
|
||
BUILD_CANCELLED: LEGACY_BUILD_FAILURE,
|
||
BUILD_FAILED: LEGACY_BUILD_FAILURE,
|
||
};
|
||
|
||
export function describeWorksPublishFailure(
|
||
code: string | null,
|
||
statusCode?: number,
|
||
): WorksPublishFailure {
|
||
const knownCode = code?.trim().toUpperCase();
|
||
if (knownCode && PUBLISH_FAILURES[knownCode]) {
|
||
return PUBLISH_FAILURES[knownCode];
|
||
}
|
||
if (statusCode === 401) return PUBLISH_FAILURES.AUTH_REQUIRED;
|
||
if (statusCode === 403) return PUBLISH_FAILURES.PUBLISH_FORBIDDEN;
|
||
if (statusCode === 409) return PUBLISH_FAILURES.PROJECT_SUBMISSION_CONFLICT;
|
||
if (statusCode === 413) return PUBLISH_FAILURES.ARCHIVE_TOO_LARGE;
|
||
if (statusCode !== undefined && statusCode >= 500) {
|
||
return PUBLISH_FAILURES.WORKS_SQUARE_UNAVAILABLE;
|
||
}
|
||
if (statusCode === 400 || statusCode === 422) {
|
||
return PUBLISH_FAILURES.SOURCE_PACKAGE_REJECTED;
|
||
}
|
||
return {
|
||
title: '提交没有完成',
|
||
reason: 'Makelore 没有收到可确认的提交结果。',
|
||
nextStep: '请稍后重新提交;如果持续失败,请联系运营人员。',
|
||
};
|
||
}
|
||
|
||
export function createDefaultWorksAppId(project: { id: string; name?: string }): string {
|
||
const normalize = (value: string) => value
|
||
.toLowerCase()
|
||
.replace(/[^a-z0-9]+/g, '-')
|
||
.replace(/^-+|-+$/g, '');
|
||
const fromId = normalize(project.id);
|
||
const fallback = `makelore-${fromId || 'project'}`.slice(0, 80).replace(/-+$/g, '');
|
||
return WORKS_APP_ID_PATTERN.test(fallback) ? fallback : 'makelore-project';
|
||
}
|