修复画布生成配方恢复边界
兼容生成配方未来字段与历史完美像素快照 补齐参数回退、旧版恢复告警和本地参考图上传 收紧生成任务幂等比较与裁扩来源归属 同步画布编辑器设计文档和回归测试
This commit is contained in:
@@ -24,21 +24,20 @@ export const CANVAS_GENERATION_ACTIONS = [
|
||||
'image.crop-expand',
|
||||
] as const satisfies readonly CanvasGenerationAction[];
|
||||
|
||||
export const REMIXABLE_CANVAS_GENERATION_ACTIONS =
|
||||
new Set<CanvasGenerationAction>([
|
||||
'image.generate',
|
||||
'spec.generate',
|
||||
'character.generate',
|
||||
'icon.generate',
|
||||
'ui-design.generate',
|
||||
'publication.generate',
|
||||
'video.generate',
|
||||
'audio.sound-effect.generate',
|
||||
'audio.background-music.generate',
|
||||
'character-animation.generate',
|
||||
'image.edit',
|
||||
'ui-design.extract-assets',
|
||||
]);
|
||||
const REMIXABLE_CANVAS_GENERATION_ACTIONS = new Set<CanvasGenerationAction>([
|
||||
'image.generate',
|
||||
'spec.generate',
|
||||
'character.generate',
|
||||
'icon.generate',
|
||||
'ui-design.generate',
|
||||
'publication.generate',
|
||||
'video.generate',
|
||||
'audio.sound-effect.generate',
|
||||
'audio.background-music.generate',
|
||||
'character-animation.generate',
|
||||
'image.edit',
|
||||
'ui-design.extract-assets',
|
||||
]);
|
||||
|
||||
const CANVAS_GENERATION_ACTION_SET = new Set<string>(CANVAS_GENERATION_ACTIONS);
|
||||
const V2_GENERATION_INPUT_KEYS = new Set([
|
||||
@@ -95,8 +94,9 @@ export function isCanvasGenerationAction(
|
||||
return typeof value === 'string' && CANVAS_GENERATION_ACTION_SET.has(value);
|
||||
}
|
||||
|
||||
export function isNormalizedCanvasGenerationInputsStructure(
|
||||
function hasNormalizedCanvasGenerationInputsStructure(
|
||||
value: unknown,
|
||||
strictWhitelist: boolean,
|
||||
): value is CanvasGenerationInputs & {
|
||||
version: 2;
|
||||
action: CanvasGenerationAction;
|
||||
@@ -114,7 +114,7 @@ export function isNormalizedCanvasGenerationInputsStructure(
|
||||
value.fields.every(
|
||||
(field) =>
|
||||
isRecord(field) &&
|
||||
hasOnlyKeys(field, V2_GENERATION_FIELD_KEYS) &&
|
||||
(!strictWhitelist || hasOnlyKeys(field, V2_GENERATION_FIELD_KEYS)) &&
|
||||
isPresentString(field.id) &&
|
||||
typeof field.title === 'string' &&
|
||||
isGenerationInputValue(field.value),
|
||||
@@ -122,7 +122,8 @@ export function isNormalizedCanvasGenerationInputsStructure(
|
||||
value.references.every(
|
||||
(reference) =>
|
||||
isRecord(reference) &&
|
||||
hasOnlyKeys(reference, V2_GENERATION_REFERENCE_KEYS) &&
|
||||
(!strictWhitelist ||
|
||||
hasOnlyKeys(reference, V2_GENERATION_REFERENCE_KEYS)) &&
|
||||
isPresentString(reference.id) &&
|
||||
typeof reference.title === 'string' &&
|
||||
(reference.label === undefined ||
|
||||
@@ -134,21 +135,46 @@ export function isNormalizedCanvasGenerationInputsStructure(
|
||||
);
|
||||
}
|
||||
|
||||
export function isNormalizedCanvasGenerationInputsStructure(
|
||||
value: unknown,
|
||||
): value is CanvasGenerationInputs & {
|
||||
version: 2;
|
||||
action: CanvasGenerationAction;
|
||||
} {
|
||||
return hasNormalizedCanvasGenerationInputsStructure(value, true);
|
||||
}
|
||||
|
||||
export function isRemixableCanvasGenerationAction(
|
||||
action: CanvasGenerationAction,
|
||||
) {
|
||||
return REMIXABLE_CANVAS_GENERATION_ACTIONS.has(action);
|
||||
}
|
||||
|
||||
function cloneV2GenerationInputs(
|
||||
value: Record<string, unknown>,
|
||||
strictWhitelist: boolean,
|
||||
): CanvasGenerationInputs | null {
|
||||
if (
|
||||
(strictWhitelist && !hasOnlyKeys(value, V2_GENERATION_INPUT_KEYS)) ||
|
||||
!isNormalizedCanvasGenerationInputsStructure(value)
|
||||
!hasNormalizedCanvasGenerationInputsStructure(value, strictWhitelist)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
version: 2,
|
||||
action: value.action,
|
||||
fields: value.fields.map((field) => ({ ...field })),
|
||||
references: value.references.map((reference) => ({ ...reference })),
|
||||
fields: value.fields.map((field) => ({
|
||||
id: field.id,
|
||||
title: field.title,
|
||||
value: field.value,
|
||||
})),
|
||||
references: value.references.map((reference) => ({
|
||||
id: reference.id,
|
||||
title: reference.title,
|
||||
...(reference.label === undefined ? {} : { label: reference.label }),
|
||||
refType: reference.refType,
|
||||
refId: reference.refId,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -170,32 +196,42 @@ function hydrateLegacyGenerationInputs(
|
||||
if (
|
||||
!isRecord(field) ||
|
||||
(strictWhitelist && !hasOnlyKeys(field, LEGACY_GENERATION_FIELD_KEYS)) ||
|
||||
!isPresentString(field.title) ||
|
||||
!isPresentString(field.value)
|
||||
(strictWhitelist
|
||||
? typeof field.title !== 'string'
|
||||
: !isPresentString(field.title)) ||
|
||||
(strictWhitelist
|
||||
? typeof field.value !== 'string'
|
||||
: !isPresentString(field.value))
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
return [{ title: field.title, value: field.value }];
|
||||
return [{ title: field.title as string, value: field.value as string }];
|
||||
});
|
||||
const references = rawReferences.flatMap((reference) => {
|
||||
if (
|
||||
!isRecord(reference) ||
|
||||
(strictWhitelist &&
|
||||
!hasOnlyKeys(reference, LEGACY_GENERATION_REFERENCE_KEYS)) ||
|
||||
!isPresentString(reference.title) ||
|
||||
!isPresentString(reference.label) ||
|
||||
(strictWhitelist
|
||||
? typeof reference.title !== 'string'
|
||||
: !isPresentString(reference.title)) ||
|
||||
(strictWhitelist
|
||||
? typeof reference.label !== 'string'
|
||||
: !isPresentString(reference.label)) ||
|
||||
(reference.refType !== 'project-resource' &&
|
||||
reference.refType !== 'asset') ||
|
||||
!isPresentString(reference.refId)
|
||||
(strictWhitelist
|
||||
? typeof reference.refId !== 'string'
|
||||
: !isPresentString(reference.refId))
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
title: reference.title,
|
||||
label: reference.label,
|
||||
title: reference.title as string,
|
||||
label: reference.label as string,
|
||||
refType: reference.refType,
|
||||
refId: reference.refId,
|
||||
refId: reference.refId as string,
|
||||
} satisfies CanvasGenerationInputReference,
|
||||
];
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user