feat(canvas): retain explicit migration eligibility at the entry boundary
This commit is contained in:
@@ -14,15 +14,19 @@ export async function loadCanvasEntry(options: { request: CanvasRequest; workspa
|
||||
options.signal?.throwIfAborted();
|
||||
if (board.kind !== 'board' || !matchesRef(board, options.boardRef) || !board.workspace_id || !profile.id) throw new Error('invalid_reference');
|
||||
if(!options.readModel&&!options.transport)throw new Error('model_loader_unavailable');
|
||||
let migration:Extract<ModelLoad,{status:'migration-required'}>|undefined;
|
||||
try {
|
||||
const model = options.readModel?await options.readModel({accountId:profile.id,workspaceId:board.workspace_id,canvasId:board.id,signal:options.signal,navigationGeneration:options.navigationGeneration??0,...(options.onPreview?{onPreview:(preview:LiveModelPreview)=>options.onPreview!(preview,board,profile)}:{})}):await loadFoundryModel(options.transport!(profile.id), board.workspace_id, board.id, options.registry, options.signal);
|
||||
options.signal?.throwIfAborted();
|
||||
if (model.status === 'ready') return { mode: 'headless' as const, board, profile, model };
|
||||
// Existing projects keep their current editor until the user explicitly migrates them.
|
||||
// This read describes eligibility, not a frozen write request: the migration
|
||||
// action must drain legacy writers and refresh source versions before sending.
|
||||
migration=model;
|
||||
} catch (error) {
|
||||
options.signal?.throwIfAborted();
|
||||
if ((error as { code?: string }).code !== 'not_found') throw error;
|
||||
}
|
||||
const snapshot = decodeSnapshot(await options.request(path + '/canvas', { signal: options.signal, headers: { Accept: CANVAS_ACCEPT } }));
|
||||
return { mode: 'legacy' as const, board, profile, snapshot };
|
||||
return { mode: 'legacy' as const, board, profile, snapshot,...(migration?{migration}:{}) };
|
||||
}
|
||||
|
||||
@@ -23,3 +23,14 @@ test('explicit migration requirement or missing deployment keeps legacy reads; p
|
||||
const f = fixture({ status: 403, body: { error: 'forbidden' } });
|
||||
await assert.rejects(() => loadCanvasEntry(f.options), /forbidden/); assert.equal(f.calls.some(path => path.endsWith('/canvas')), false);
|
||||
});
|
||||
|
||||
test('legacy entry retains verified migration root and the full source vector without offering migration for an unavailable API',async()=>{
|
||||
const child=id(4),f=fixture({status:409,body:{error:'canonical_migration_required',root_id:board.id,document_format_version:1,source_versions:{[board.id]:3,[child]:7}}});
|
||||
const result=await loadCanvasEntry(f.options);
|
||||
assert.equal(result.mode,'legacy');assert.equal(result.migration.rootId,board.id);
|
||||
assert.deepEqual([...result.migration.sourceVersions],[[board.id,3],[child,7]]);assert.equal(result.migration.sourceVersions.set,undefined);
|
||||
assert.equal(result.profile.id,profile.id);assert.ok(result.snapshot);
|
||||
assert.equal(f.calls.some(path=>path.endsWith('/migrate')),false);
|
||||
const unavailable=await loadCanvasEntry(fixture({status:404,body:{error:'not_found'}}).options);
|
||||
assert.equal(unavailable.mode,'legacy');assert.equal(unavailable.migration,undefined);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user