19 lines
3.0 KiB
JavaScript
19 lines
3.0 KiB
JavaScript
'use strict';
|
|||
|
|
// Restoration creates managed native tabs; it never imports renderer pageState.
|
||
|
|
async function restoreNativeSession(tabs,{activeIndex,signal,assertCurrent,revision,owner},{registry,store,create,activate,traceOperation=async(_name,fn)=>fn()}={}){
|
||
|
|
if(!Array.isArray(tabs)||!tabs.length||tabs.length>32||!Number.isInteger(activeIndex)||activeIndex<0||activeIndex>=tabs.length||typeof assertCurrent!=='function')throw Error('Invalid managed session restore');
|
||
|
|
const originals=registry.all().map(tab=>({id:tab.id,contents:tab.contents})),initialActive=registry.active()?.id,created=[];let expectedActive=initialActive;const epoch=()=>{const value=registry.context(registry.active()?.id,{foreground:true}).activation;if(!Number.isSafeInteger(value)||value<0)throw Error('Managed restore activation unavailable');return value;};let expectedEpoch=epoch();const ownActivation=id=>{const next=epoch(),required=expectedEpoch+(id===expectedActive?0:1);if(next!==required||registry.active()?.id!==id)throw Error('Managed restore activation changed');expectedActive=id;expectedEpoch=next;};
|
||
|
|
const current=()=>{
|
||
|
|
assertCurrent();if(signal?.aborted||store.list(owner).revision!==revision||registry.all().length!==originals.length+created.length||registry.active()?.id!==expectedActive||epoch()!==expectedEpoch)throw Error('Managed restore context changed');
|
||
|
|
for(const row of [...originals,...created])if(registry.get(row.id).contents!==row.contents||row.contents.isDestroyed())throw Error('Managed restore tab changed');
|
||
|
|
};
|
||
|
|
await traceOperation('preflight',()=>{current();if(originals.length+tabs.length>32)throw Error('Native tab capacity reached');});
|
||
|
|
try{
|
||
|
|
for(const tab of tabs){current();const result=await traceOperation('native',()=>create({url:tab.url,...(tab.group?{group:tab.group}:{})},{signal,assertCurrent:current,onCreated:native=>{if(!native||originals.some(row=>row.id===native.id)||created.some(row=>row.id===native.id)||!registry.all().some(row=>row.id===native.id&&row.contents===native.contents))throw Error('Managed restore ownership mismatch');created.push({id:native.id,contents:native.contents});},onActivated:native=>ownActivation(native.id)}));current();if(result!==created.at(-1)?.id)throw Error('Managed restore tab mismatch');}
|
||
|
|
current();activate(created[activeIndex].id);ownActivation(created[activeIndex].id);current();return {completed:true,sessionIds:created.map(row=>row.id),activeSessionId:expectedActive};
|
||
|
|
}catch(error){
|
||
|
|
// Closing owned tabs cannot undo network requests already sent before failure.
|
||
|
|
let rollbackFailed=false;await traceOperation('rollback',()=>{for(const row of [...created].reverse()){try{if(registry.all().some(tab=>tab.id===row.id&&tab.contents===row.contents))registry.remove(row.id);}catch{rollbackFailed=true;}}try{if(initialActive&®istry.all().some(tab=>tab.id===initialActive&&!tab.contents.isDestroyed()))activate(initialActive);}catch{rollbackFailed=true;}});if(rollbackFailed)throw Error('Managed session rollback incomplete',{cause:error});throw error;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
module.exports={restoreNativeSession};
|