Files
makelore/src/lib/works-project-publish.ts

262 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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';
}