if(targetIndex<0)thrownewError('Could not find revision: '+revision.id);
if(targetIndex!==revs.length-1){
revs=revs.slice();
consttoTop=revs[targetIndex];
revs.splice(targetIndex,1);
revs.push(toTop);
}
returnrevs;
}
// Note: revs must be sorted by update_time ASC (as returned by allByType)
staticasyncmergeDiffs(revision,revs=null){
if(!('encryption_applied'inrevision)||!!revision.encryption_applied)thrownewJoplinError('Target revision is encrypted','revision_encrypted');
if(!revs){
revs=awaitthis.modelSelectAll('SELECT * FROM revisions WHERE item_type = ? AND item_id = ? AND item_updated_time <= ? ORDER BY item_updated_time ASC',[
revision.item_type,
revision.item_id,
revision.item_updated_time,
]);
}else{
revs=revs.slice();
}
// Handle rare case where two revisions have been created at exactly the same millisecond
// Also handle even rarer case where a rev and its parent have been created at the
// same milliseconds. All code below expects target revision to be on top.
revs=this.moveRevisionToTop(revision,revs);
constoutput={
title:'',
body:'',
metadata:{},
};
// Build up the list of revisions that are parents of the target revision.
constrevIndexes=[revs.length-1];
letparentId=revision.parent_id;
for(leti=revs.length-2;i>=0;i--){
constrev=revs[i];
if(rev.id!==parentId)continue;
parentId=rev.parent_id;
revIndexes.push(i);
}
revIndexes.reverse();
for(constrevIndexofrevIndexes){
constrev=revs[revIndex];
if(!!rev.encryption_applied)thrownewJoplinError(sprintf('Revision "%s" is encrypted',rev.id),'revision_encrypted');
// When deleting old revisions, we need to make sure that the oldest surviving revision
// is a "merged" one (as opposed to a diff from a now deleted revision). So every time
// we deleted a revision, we need to find if there's a corresponding surviving revision
// and modify that revision into a "merged" one.
constcutOffDate=Date.now()-ttl;
constrevisions=awaitthis.modelSelectAll('SELECT * FROM revisions WHERE item_updated_time < ? ORDER BY item_updated_time DESC',[cutOffDate]);
constdoneItems={};
for(constrevofrevisions){
constdoneKey=rev.item_type+'_'+rev.item_id;
if(doneItems[doneKey])continue;
constkeptRev=awaitthis.modelSelectOne('SELECT * FROM revisions WHERE item_updated_time >= ? AND item_type = ? AND item_id = ? ORDER BY item_updated_time ASC LIMIT 1',[
cutOffDate,
rev.item_type,
rev.item_id,
]);
try{
constdeleteQueryCondition='item_updated_time < ? AND item_id = ?';
constdeleteQueryParams=[cutOffDate,rev.item_id];
constdeleteQuery={sql:'DELETE FROM revisions WHERE '+deleteQueryCondition,params:deleteQueryParams};
if(!keptRev){
consthasEncrypted=awaitthis.modelSelectOne('SELECT * FROM revisions WHERE encryption_applied = 1 AND '+deleteQueryCondition,deleteQueryParams);
if(!!hasEncrypted)thrownewJoplinError('One of the revision to be deleted is encrypted','revision_encrypted');