feat: 增加固定工作台应用目录兼容格式

This commit is contained in:
2026-10-13 16:48:14 +08:00
parent 70f3132ec4
commit 0c0f991baa
21 changed files with 2408 additions and 19 deletions

View File

@@ -0,0 +1,32 @@
/** 作者材料的跨字段检查;身份与运行事实由客户端owner核验,不执行安装说明。 */
export function validateApplicationReleaseContract(manifest) {
if (manifest.type !== 'web-bundle' && manifest.appRelease === undefined) return []
const errors = []
if (!['web-bundle', 'native-app', 'docker-app'].includes(manifest.type)) errors.push('appRelease: unsupported delivery type')
if (!manifest.appRelease?.requiredCapabilities?.includes('app-authoring-v1')) errors.push('appRelease: app-authoring-v1 capability handshake required')
if (!manifest.appRelease?.requiredCapabilities?.includes('release') ||
(manifest.type === 'web-bundle' && !manifest.appRelease.requiredCapabilities.includes('web-bundle'))) errors.push('appRelease: delivery capability handshake required')
const images = manifest.appRelease?.imageRefs
if (manifest.type === 'docker-app' && (!Array.isArray(images) || !images.length || images.some(image =>
typeof image !== 'string' || !/^[a-z0-9][a-z0-9._:/-]*@sha256:[a-f0-9]{64}$/.test(image) ||
Number(image.split('/')[0].split(':')[1] ?? 0) > 65535))) errors.push('appRelease: Docker images must be pinned by digest')
if (manifest.type !== 'docker-app' && images !== undefined) errors.push('appRelease: imageRefs belongs only to Docker release')
const source = manifest.source
if (!source || !['git', 'release'].includes(source.kind) ||
!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(source.ref ?? '') || !/^[a-f0-9]{64}$/.test(source.sha256 ?? '')) {
errors.push('source: authored release requires immutable commit and archive sha256')
}
try {
const url = new URL(source?.url)
if (url.protocol !== 'https:' || url.username || url.password || url.hash || url.search) errors.push('source: credential-free fixed HTTPS URL required')
} catch { errors.push('source: invalid URL') }
if (source?.kind === 'git' && !isPortablePath(source.path)) errors.push('source: git archive path must be canonical and relative')
if (!isPortablePath(manifest.appRelease?.definitionPath)) errors.push('appRelease: definitionPath must be canonical and relative')
return errors
}
function isPortablePath(value) {
return typeof value === 'string' && value.length > 0 && value.length <= 512 &&
!/[\\\x00-\x1f?#:]/.test(value) && !value.startsWith('/') &&
value.split('/').every(part => part.length > 0 && part !== '.' && part !== '..')
}