发布用 packageAll 落定的权威表单合成 metadata
- useTaonierExport 维护 effectiveFormRef,refresh 采纳新表单与打包前 flush 成功时同步 - 暴露 getEffectiveForm;publishGame 在 await packageAll() 之后用它,避免旧快照与宿主读盘不一致 - 补两条单测:采纳新表单后权威表单更新、未保存输入先落盘再作为权威表单
This commit is contained in:
+14
@@ -261,6 +261,14 @@ export function useTaonierExport({
|
||||
/** 上一次抛进错误池的故障指纹:同一个只抛一次,见 [useTaonierExport.throwToErrorPool]。 */
|
||||
const reportedFailureRef = useRef('');
|
||||
const formRef = useRef<TaonierExportForm | null>(null);
|
||||
/**
|
||||
* 已经落定到磁盘的权威表单:`refresh` 采纳新内容、`packageAll` flush 成功后同步。
|
||||
*
|
||||
* 发布链路在 `await packageAll()` 之后必须用它合成 metadata:`packageAll` 内部的 `refresh`
|
||||
* 可能在用户无未保存输入时采纳一份更新的注册表表单,而宿主发布命令读的正是这份磁盘表单;
|
||||
* 继续用 await 之前的快照会让文字字段与媒体字节对不上。
|
||||
*/
|
||||
const effectiveFormRef = useRef<TaonierExportForm | null>(null);
|
||||
/**
|
||||
* 当前项目路径的即时值。异步结果(读内容、取指纹、保存、构建、读线上值)回来时用它判断结果还
|
||||
* 属不属于当前项目:面板根跨项目不卸载,旧请求回来时绝不能把上一个项目的表单 / 基线 / 线上值写
|
||||
@@ -309,6 +317,7 @@ export function useTaonierExport({
|
||||
initialReadSettledRef.current = false;
|
||||
reportedFailureRef.current = '';
|
||||
formRef.current = null;
|
||||
effectiveFormRef.current = null;
|
||||
}, [projectPath]);
|
||||
|
||||
// 脚本出现之后就把提示重新武装:它要是又被删掉,用户该再看到一次阻断提示。
|
||||
@@ -379,6 +388,7 @@ export function useTaonierExport({
|
||||
return content.hasScript;
|
||||
}
|
||||
setForm(content.form);
|
||||
effectiveFormRef.current = content.form;
|
||||
setFieldErrors([]);
|
||||
setConflict(null);
|
||||
// 这里不再清 `failure`:上面的函数式清理只清读来源的失败,构建类失败是那次构建的现场,
|
||||
@@ -766,6 +776,8 @@ export function useTaonierExport({
|
||||
if (dirty && form && !conflict && fieldErrors.length === 0) {
|
||||
const flushed = await persist(form, baselineRef.current);
|
||||
if (!flushed) return false;
|
||||
// flush 成功后磁盘上的就是这份;发布链路要用它而不是调用前的旧快照。
|
||||
effectiveFormRef.current = form;
|
||||
}
|
||||
// 没解决的冲突 / 字段红字:盘上的内容和用户看到的不一致,先别打包。
|
||||
if (conflict || fieldErrors.length > 0) return false;
|
||||
@@ -907,6 +919,8 @@ export function useTaonierExport({
|
||||
adoptOnlineText,
|
||||
resolveConflict,
|
||||
packageAll,
|
||||
/** `await packageAll()` 之后取「这次真正落定的表单」;还没落定时返回 `null`。 */
|
||||
getEffectiveForm: () => effectiveFormRef.current,
|
||||
adapt,
|
||||
repair,
|
||||
fillForm,
|
||||
|
||||
+4
-1
@@ -235,7 +235,10 @@ export function useTaonierTab({
|
||||
// 订阅失败只影响阶段条:发布本身照常进行,按钮上仍显示「正在发布…」。
|
||||
}
|
||||
try {
|
||||
const metadata = buildTaonierPublishMetadata(form, online);
|
||||
// `packageAll` 内部可能采纳一份更新的注册表表单(并已落盘),宿主发布命令读的正是那份;
|
||||
// 拿它合成 metadata,避免文字字段来自 await 之前的旧快照、与媒体字节对不上。
|
||||
const effectiveForm = taonier.getEffectiveForm() ?? form;
|
||||
const metadata = buildTaonierPublishMetadata(effectiveForm, online);
|
||||
const next = await publishLocalProjectGame({
|
||||
invoke,
|
||||
projectPath,
|
||||
|
||||
@@ -511,6 +511,74 @@ describe('hook 状态', () => {
|
||||
expect(result.current.failure?.type).toBe('commandFailed');
|
||||
});
|
||||
|
||||
it('packageAll 采纳新表单后,发布取到的权威表单跟着更新', async () => {
|
||||
let registryForm: TaonierExportForm = FORM;
|
||||
const invoke = vi.fn(async (command: string) => {
|
||||
switch (command) {
|
||||
case 'read_taonier_export':
|
||||
return { form: registryForm, hasScript: true };
|
||||
case 'read_taonier_export_hash':
|
||||
return CONTENT_HASH;
|
||||
case 'read_game_distribution_publication':
|
||||
return ONLINE_FIRST_PUBLISH;
|
||||
case 'save_taonier_export_form':
|
||||
return 'hash-2';
|
||||
case 'run_taonier_export_build':
|
||||
return { outputTail: '', omittedCharacters: 0 };
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
installTauriRuntime({ core: { invoke } });
|
||||
const { result } = renderHook(() =>
|
||||
useTaonierExport({
|
||||
enabled: true,
|
||||
projectPath: PROJECT_PATH,
|
||||
submitTurn: vi.fn(async () => true),
|
||||
}),
|
||||
);
|
||||
await waitFor(() => expect(result.current.form).toEqual(FORM));
|
||||
|
||||
// 面板背后注册表被改(例如 agent 重写 .export/taonier.json)。用户没有未保存输入时,
|
||||
// packageAll 的 refresh 会采纳这份新表单;发布必须拿同一份合成 metadata,否则文字字段
|
||||
// 会停在 await 之前的旧快照,与宿主读盘拿到的媒体字节对不上。
|
||||
registryForm = { ...FORM, title: '项目里的新名字' };
|
||||
await act(async () => {
|
||||
await result.current.packageAll();
|
||||
});
|
||||
|
||||
expect(result.current.form?.title).toBe('项目里的新名字');
|
||||
expect(result.current.getEffectiveForm()?.title).toBe('项目里的新名字');
|
||||
});
|
||||
|
||||
it('有未保存输入时 packageAll 先落盘,权威表单就是这份输入', async () => {
|
||||
const invoke = createInvoke();
|
||||
installTauriRuntime({ core: { invoke } });
|
||||
const { result } = renderHook(() =>
|
||||
useTaonierExport({
|
||||
enabled: true,
|
||||
projectPath: PROJECT_PATH,
|
||||
submitTurn: vi.fn(async () => true),
|
||||
}),
|
||||
);
|
||||
await waitFor(() => expect(result.current.form).toEqual(FORM));
|
||||
|
||||
await act(async () => {
|
||||
result.current.updateField('title', '刚敲的名字');
|
||||
});
|
||||
await act(async () => {
|
||||
await result.current.packageAll();
|
||||
});
|
||||
|
||||
expect(invoke).toHaveBeenCalledWith(
|
||||
'save_taonier_export_form',
|
||||
expect.objectContaining({
|
||||
form: expect.objectContaining({ title: '刚敲的名字' }),
|
||||
}),
|
||||
);
|
||||
expect(result.current.getEffectiveForm()?.title).toBe('刚敲的名字');
|
||||
});
|
||||
|
||||
it('校验不过时停在表单:不落盘、不跑构建(校验不过不触宿主)', async () => {
|
||||
const invoke = createInvoke();
|
||||
installTauriRuntime({ core: { invoke } });
|
||||
|
||||
Reference in New Issue
Block a user