Includes:
1) Better error handling and move support
2) An upgradeable launcher with signatures to avoid having to download 70MB just
to get the latest 200kb of javascript
3) Data stored in a data file instead of in the program, to allow for modifications
log(chalk.cyan(`Waiting for new saves to appear in ${chalk.cyanBright.bold(sourceDir)}`))
log(chalk.green(`Waiting for new saves with filenames like ${chalk.greenBright.bold(`/${sourceRegexpString}/`)}to appear in ${chalk.greenBright.bold(sourceDir)}`))
log(chalk.green(`Scanning for existing saves with filenames like ${chalk.greenBright.bold(`/${sourceRegexpString}/`)} in ${chalk.greenBright.bold(sourceDir)}`))
constdir=awaitopendir(sourceDir)
forawait(constdirentofdir){
if(dirent.isFile()&&sourceRe.test(dirent.name)){
yielddirent.name
}
}
log(chalk.cyan(`Finished scanning for existing saves`))
}
asyncfunctionpromptForFilename(){
constsaveType=awaitnewSelect({
asyncfunctionpromptForFilename({
times,
voreTypes,
voreRoles,
voreResults,
characterSceneTypes,
otherSceneTypes,
characters
}){
constsaveType=awaitnewAutoComplete({
message:"What type of save is this?",
choices:["Useful Save","Vore Scene","Skip This Save"],
limit:10,
choices:["Useful Save","Vore Scene",...(Array.isArray(characterSceneTypes)&&characterSceneTypes.length>0?characterSceneTypes.map((item)=>`${item} (Character Scene)`):[]),...(Array.isArray(otherSceneTypes)&&otherSceneTypes.length>0?otherSceneTypes.map((item)=>`${item} (Other Scene)`):[]),"Skip This Save"],
message:`What is the comment for this ${sceneType} scene?`,
validate:validateFilenamePart
}).run()
return`${sceneType} - ${time} - ${title}.save`
}else{
returnnull
}
}
asyncfunctionshouldDoRename(destPath){
try{
awaitaccess(destPath)
asyncfunctionaskIfOverwrite(destPath){
console.log(chalk.yellow(`A file named ${chalk.yellowBright.bold(destPath)} already exists.`))
}catch(ex){
if(ex.code==="ENOENT"){
returntrue
}else{
console.log(chalk.red(`The file named ${chalk.redBright.bold(destPath)} could not be accessed: ${chalk.redBright.bold(ex)}`))
}
}
constresult=awaitnewSelect({message:"What should we do?",choices:["Skip it","Rename it","Overwrite it"]}).run();
constresult=awaitnewSelect({
message:"What should we do?",
choices:["Skip it","Rename it","Overwrite it"]
}).run()
switch(result){
case"Skip it":
returnfalse;
returnfalse
case"Overwrite it":
returntrue;
returntrue
case"Rename it":
return(awaitnewInput({message:"What would you like to name the file?",initial:basename(destPath,".save")}).run())+".save"
return(awaitnewInput({
message:"What would you like to name the file?",
initial:basename(destPath,".save"),
validate:(name)=>name.length>0&&isValidFilename(name)?true:"must be a nonempty string not ending in trailing periods or containing any of the characters <>:\"/\\|?*"
log(chalk.red(`Something went wrong: ${chalk.redBright.bold(ex)}`))
constgiveUp=awaitnewToggle({
message:"Want to keep trying?",
disabled:"Yes, try again",
enabled:"No, skip this save"
}).run()
if(giveUp){
break
}
}
}
if(!destPath){
log(chalk.yellow("Skipped."))
filequeue.shift()
continue
}
awaitrename(nextFile,destPath)
log(chalk.cyan(`Moved to ${chalk.cyanBright.bold(destPath)}`))
filequeue.shift()
}
processingFiles=false
if(endAfterCompleting){
resolvePromise()
}
}
log(chalk.green(`Ready to move renamed saves to ${chalk.greenBright.bold(destDir)}`))
return{
push(...files){
if(!rejectPromise){
@ -253,49 +512,69 @@ function queueMovingTo(destDir) {
filequeue.push(file)
}
}
processFiles().catch(()=>{
processFiles().catch((err)=>{
if(!rejectPromise){
return
}
rejectPromise()
rejectPromise(err)
resolvePromise=null
rejectPromise=null
})
},
complete(){
shutDown(){
if(!resolvePromise){
throwError("Already stopped the queue")
return
}
if(processingFiles){
endAfterCompleting=true
}else{
resolvePromise()
resolvePromise=null
rejectPromise=null
}
},
completionPromise
}
}
asyncfunctionmain(){
const{config}=envPaths("SMVA-Indexer")
exportasyncfunctionmain(){
log(chalk.green(`${packageData.name}${chalk.greenBright.bold(`v${packageData.version}`)} - built at ${chalk.greenBright.bold(newDate(buildTimestamp).toISOString())}`))
"description":"Simple SMVA save file templating system.",
"bin":"indexer.mjs",
"scripts":{
"start":"node indexer.mjs",
"build":"npm run build-rollup && concurrently -i -n Win,Mac,Lin -c blue,white,yellow \"npm run build-win-nexe\" \"npm run build-mac-nexe\" \"npm run build-lin-nexe\"",
"build":"concurrently -n Rollup,Data,Signer -s all -c green,yellow,red \"npm run build-rollup\" \"npm run build-data\" \"npm run build-signer\" && npm run build-nexe && npm run sign",
"build-signed":"concurrently -n Rollup,Signer -c green,red \"npm run build-rollup\" \"npm run build-signer\" && npm run sign",
"build-nexe":"concurrently -i -s all -n Win,Mac,Lin -c blue,white,yellow \"npm run build-win-nexe\" \"npm run build-mac-nexe\" \"npm run build-lin-nexe\"",
process.stderr.write(chalk.cyan(`Successfully signed ${chalk.cyanBright.bold(bundlePath)} as ${chalk.cyanBright.bold(destinationPath)} with ${chalk.cyanBright.bold(keyPath)}\n`))
}catch(ex){
process.stderr.write(chalk.red(`Failed to sign ${chalk.redBright.bold(bundlePath)} as ${chalk.redBright.bold(destinationPath)} with ${chalk.redBright.bold(keyPath)}: ${chalk.redBright.bold(ex)}\n`))