fix: harden upload resilience and contracts

This commit is contained in:
Tom Boullay
2026-05-12 23:49:30 +02:00
parent 101af23418
commit 606df93b69
19 changed files with 479 additions and 159 deletions
+61 -16
View File
@@ -7,6 +7,7 @@ import { compressTextureBuffer } from '@/lib/texture-compression'
import { classifyAssetCategory } from '@/lib/asset-classification'
import { normalizeTextureFilename } from '@/lib/asset-naming'
import { TEXTURE_EXTENSIONS, TMP_DIR } from '@/lib/constants'
import { getErrorMessage, isRecord } from '@/lib/guards'
import { getModelAssetPath } from '@/lib/model-paths'
import type { GitModelMode, ParsedFile, PreparedAssetSummary, PreparedGitAssetsResult, PushFile } from '@/lib/types'
@@ -18,6 +19,30 @@ interface PrepareGitAssetsParams {
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
function isJsonValue(value: unknown): value is JsonValue {
if (value === null) return true
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return true
}
if (Array.isArray(value)) {
return value.every(isJsonValue)
}
return isRecord(value) && Object.values(value).every(isJsonValue)
}
function parseJsonValue(content: string) {
const parsed: unknown = JSON.parse(content)
if (!isJsonValue(parsed)) {
throw new Error('model.gltf contient un JSON invalide')
}
return parsed
}
function getTextureFilenameMap(parsedFiles: ParsedFile[]) {
const filenameMap = new Map<string, string>()
const normalizedGroups = new Map<string, Array<{ original: string; normalized: string }>>()
@@ -75,7 +100,7 @@ function rewriteGltfUris(value: JsonValue, filenameMap: Map<string, string>): Js
function prepareModelBuffer(buffer: Buffer, filenameMap: Map<string, string>) {
if (filenameMap.size === 0) return buffer
const parsed = JSON.parse(buffer.toString('utf-8')) as JsonValue
const parsed = parseJsonValue(buffer.toString('utf-8'))
return Buffer.from(JSON.stringify(rewriteGltfUris(parsed, filenameMap), null, 2), 'utf-8')
}
@@ -130,7 +155,14 @@ async function prepareSeparateFiles(
})
}
return { filesToPush, modelFilename, assetSummaries, compressed, compressionError }
return {
filesToPush,
modelFilename,
assetSummaries,
compressed,
compressionError,
deliveryMode: 'keep-gltf' as const,
}
}
async function prepareDracoGlb(
@@ -160,22 +192,35 @@ async function prepareDracoGlb(
await writeFile(join(tmpFolder, filename), content)
}
const result = await compressWithBlender(inputModelPath, outputModelPath)
if (!result.success || !existsSync(outputModelPath)) {
throw new Error(result.error || 'Compression Blender echouee')
}
try {
const result = await compressWithBlender(inputModelPath, outputModelPath)
if (!result.success || !existsSync(outputModelPath)) {
throw new Error(result.error || 'Compression Blender echouee')
}
const content = await readFile(outputModelPath)
const modelFilename = 'model.glb'
const content = await readFile(outputModelPath)
const modelFilename = 'model.glb'
return {
filesToPush: [{
path: getModelAssetPath(folderName, modelFilename),
contentBase64: content.toString('base64'),
}],
modelFilename,
assetSummaries: [{ filename: modelFilename, kind: 'model', compressed: true }],
compressed: true,
return {
filesToPush: [{
path: getModelAssetPath(folderName, modelFilename),
contentBase64: content.toString('base64'),
}],
modelFilename,
assetSummaries: [{ filename: modelFilename, kind: 'model', compressed: true }],
compressed: true,
deliveryMode: 'draco-glb',
}
} catch (err) {
const fallback = await prepareSeparateFiles(folderName, parsedFiles, textureFilenameMap)
const message = getErrorMessage(err, 'Compression Blender echouee')
return {
...fallback,
compressionError: fallback.compressionError
? `${message}. ${fallback.compressionError}`
: message,
}
}
} finally {
await rm(tmpFolder, { recursive: true, force: true }).catch(() => {})