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

291 lines
12 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 PUBLISH_FAILURES: Record<string, WorksPublishFailure> = {
PREVIEW_REQUIRED: {
title: '请先打开项目预览',
reason: '提交前检查需要当前项目正在 Makelore 内置浏览器中运行。',
nextStep: '先在内置浏览器中打开当前项目的本地预览,确认页面可用后再提交。',
},
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: {
title: '暂时无法下载项目依赖',
reason: '云端没有成功准备 package-lock.json 中的依赖。',
nextStep: '请检查 package-lock.json 是否已提交,并确认依赖名称和版本有效后重试。',
},
DEPENDENCY_PREFETCH_TIMEOUT: {
title: '下载项目依赖超时',
reason: '云端准备依赖的时间超过限制。',
nextStep: '稍后重试;如果持续失败,请让开发助手精简依赖。',
},
BUILD_COMMAND_FAILED: {
title: '项目没有构建成功',
reason: '云端执行项目构建时发现代码或配置错误。',
nextStep: '请让开发助手运行 npm run build修复错误后提交新版本。',
},
BUILD_TIMEOUT: {
title: '项目构建超时',
reason: '云端构建时间超过平台限制。',
nextStep: '请让开发助手减少构建步骤或过大的依赖后重试。',
},
BUILD_SANDBOX_UNAVAILABLE: {
title: '云端构建环境暂时不可用',
reason: '平台暂时无法启动安全构建环境。',
nextStep: '稍后重新提交,无需修改项目。',
},
BUILD_PIPELINE_MISMATCH: {
title: '项目发布方式不匹配',
reason: '当前提交不是平台支持的 Makelore 静态项目格式。',
nextStep: '请从当前项目重新点击一键提交,不要手工修改发布包。',
},
BUILD_PIPELINE_UNSUPPORTED: {
title: '这个项目暂不支持自动发布',
reason: '平台没有找到适合当前项目类型的安全构建方式。',
nextStep: '请联系运营人员确认项目类型。',
},
VERSION_NOT_FOUND: {
title: '平台没有找到本次提交',
reason: '构建服务无法读取刚刚上传的版本。',
nextStep: '请重新提交;如果持续失败,请联系运营人员。',
},
SOURCE_DIGEST_MISMATCH: {
title: '上传内容校验失败',
reason: '平台收到的项目内容与提交时不一致。',
nextStep: '重新提交一次;如果持续失败,请联系运营人员。',
},
OUTPUT_MISSING: {
title: '没有生成可运行页面',
reason: '构建完成后没有找到发布所需的页面文件。',
nextStep: '请让开发助手检查 Vite 输出目录和 index.html。',
},
OUTPUT_INVALID: {
title: '生成的页面无法发布',
reason: '构建输出缺少必要文件或包含不安全内容。',
nextStep: '请让开发助手检查 Vite 构建输出后重试。',
},
OUTPUT_LIMIT_EXCEEDED: {
title: '生成的页面文件太大',
reason: '构建结果超过平台允许范围。',
nextStep: '请让开发助手压缩素材、拆分资源或删除无用输出。',
},
RELEASE_STORE_FAILED: {
title: '平台暂时无法保存发布文件',
reason: '作品已经构建,但发布存储当前不可用。',
nextStep: '稍后重新提交,无需修改项目。',
},
BUILD_STALE: {
title: '构建任务已中断',
reason: '云端构建任务长时间没有继续运行。',
nextStep: '重新提交一次;如果持续发生,请联系运营人员。',
},
BROWSER_SMOKE_FAILED: {
title: '自动打开作品时发现问题',
reason: '云端浏览器检测到了白屏、运行错误或资源加载失败。',
nextStep: '请让开发助手运行 npm run build并在浏览器中检查构建结果后重试。',
},
BROWSER_SMOKE_TIMEOUT: {
title: '自动打开作品超时',
reason: '作品在规定时间内没有完成加载。',
nextStep: '请让开发助手检查首屏资源大小和启动逻辑,优化后重新提交。',
},
BROWSER_SMOKE_UNAVAILABLE: {
title: '自动验收环境暂时不可用',
reason: '平台暂时无法启动用于检查作品的安全浏览器。',
nextStep: '稍后重新提交,无需修改项目。',
},
BUILD_CANCELLED: {
title: '构建已取消',
reason: '平台没有继续处理这个版本。',
nextStep: '确认没有其他版本正在处理后重新提交。',
},
BUILD_FAILED: {
title: '项目没有构建成功',
reason: '云端未能生成可审核的作品。',
nextStep: '请让开发助手先确认 npm run build 成功,再提交新版本。',
},
};
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';
}